Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
E
exapunks-hackmatch-bot
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Taddeüs Kroes
exapunks-hackmatch-bot
Commits
b62da615
Commit
b62da615
authored
5 years ago
by
Taddeüs Kroes
Browse files
Options
Downloads
Patches
Plain Diff
Improve fragmentation/columnsize scores, fiddle with params, comment some broken code
parent
295cfec6
No related branches found
Branches containing commit
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
strategy.py
+118
-83
118 additions, 83 deletions
strategy.py
with
118 additions
and
83 deletions
strategy.py
+
118
−
83
View file @
b62da615
import
time
from
collections
import
deque
from
itertools
import
islice
from
parser
import
COLUMNS
,
NOBLOCK
,
detect_blocks
,
detect_exa
,
\
detect_held
,
print_board
,
is_basic
,
is_bomb
,
bomb_to_basic
from
itertools
import
combinations
,
islice
from
parse
import
COLUMNS
,
NOBLOCK
,
detect_blocks
,
detect_exa
,
\
detect_held
,
print_board
,
is_basic
,
is_bomb
,
bomb_to_basic
GRAB
,
DROP
,
SWAP
,
LEFT
,
RIGHT
=
range
(
5
)
GRAB
,
DROP
,
SWAP
,
LEFT
,
RIGHT
,
SPEED
=
range
(
6
)
GET
=
((
GRAB
,),
(
SWAP
,
GRAB
),
(
GRAB
,
SWAP
,
DROP
,
SWAP
,
GRAB
))
PUT
=
((
DROP
,),
(
DROP
,
SWAP
),
(
DROP
,
SWAP
,
GRAB
,
SWAP
,
DROP
))
#REVERSE = [DROP, GRAB, SWAP, RIGHT, LEFT]
MIN_BASIC_GROUP_SIZE
=
4
MIN_BOMB_GROUP_SIZE
=
2
FIND_GROUPS_DEPTH
=
20
FRAG_DEPTH
=
20
FIND_GROUPS_DEPTH
=
4
FRAG_DEPTH
=
4
COLSIZE_PRIO
=
5
COLSIZE_PRIO_HIGH
=
7
COLSIZE_MAX
=
8
BOMB_POINTS
=
2
MIN_ROWS
=
2
class
State
:
...
...
@@ -23,10 +25,12 @@ class State:
self
.
exa
=
exa
self
.
held
=
held
def
has_holes
(
self
):
return
any
(
self
.
blocks
[
i
]
==
NOBLOCK
for
col
in
self
.
iter_columns
()
for
i
in
col
)
def
grabbing_of_dropping
(
self
):
skip
=
self
.
colskip
(
self
.
exa
)
i
=
(
skip
+
1
)
*
COLUMNS
+
self
.
exa
return
i
<
len
(
self
.
blocks
)
and
self
.
blocks
[
i
]
==
NOBLOCK
#return any(len(col) > 1 and col[1] == NOBLOCK
# for col in map(tuple, self.iter_columns()))
def
iter_columns
(
self
):
nrows
=
self
.
nrows
()
...
...
@@ -54,45 +58,60 @@ class State:
for
col
in
range
(
COLUMNS
):
yield
self
.
nrows
()
-
self
.
colskip
(
col
)
#def highest_column(self):
# for i, block in enumerate(self.blocks):
# if block != NOBLOCK:
# return self.nrows() - i // COLUMNS
def
hole_score
(
self
):
def
empty_column_score
(
self
):
skip
=
0
for
i
,
block
in
enumerate
(
self
.
blocks
):
if
block
!=
NOBLOCK
:
skip
=
i
//
COLUMNS
break
nrows
=
self
.
nrows
()
score
=
0
for
col
in
range
(
COLUMNS
):
for
row
in
range
(
self
.
nrows
()):
i
=
row
*
COLUMNS
+
col
if
self
.
blocks
[
i
]
!=
NOBLOCK
:
for
row
in
range
(
skip
,
nrows
):
if
self
.
blocks
[
row
*
COLUMNS
+
col
]
!=
NOBLOCK
:
break
score
+=
row
+
1
score
+=
row
-
skip
+
1
return
score
def
score
(
self
,
points
,
moves
,
prev
):
frag
=
self
.
fragmentation
()
colsizes
=
list
(
self
.
colsizes
())
#colsizes = list(self.colsizes())
#mincol = min(colsizes)
maxcol
=
max
(
colsizes
)
colsize_score
=
maxcol
,
colsizes
.
count
(
maxcol
)
#, -mincol
#
maxcol = max(colsizes)
#
colsize_score = maxcol, colsizes.count(maxcol) #, -mincol
#colsize_score = tuple(sorted(colsizes, reverse=True))
#if prev.nrows() >= 6:
# return colsize_score, -points, frag, len(moves)
colsize_score
=
maxcol
,
self
.
hole
_score
()
#
colsize_score = maxcol, self.
empty_column
_score()
prev_colsize
=
max
(
prev
.
colsizes
())
frag
=
self
.
fragmentation
()
colsize_score
=
self
.
empty_column_score
()
#return -points, frag + colsize_score, len(moves)
if
prev_colsize
>=
COLSIZE_PRIO
:
return
colsize_score
,
frag
,
-
points
,
len
(
moves
)
frag
+=
colsize_score
prev_colsize
=
max
(
prev
.
colsizes
())
if
prev_colsize
>=
COLSIZE_PRIO_HIGH
:
return
colsize_score
,
len
(
moves
),
-
points
,
frag
elif
prev_colsize
>=
COLSIZE_PRIO
:
return
-
points
,
colsize_score
,
frag
,
len
(
moves
)
else
:
return
-
points
,
frag
,
colsize_score
,
len
(
moves
)
def
score_moves
(
self
):
# clear exploding blocks before computing colsize
prev
=
self
.
copy
()
prev
.
score_points
()
#
prev = self.copy()
#
prev.score_points()
for
moves
in
self
.
gen_moves
():
try
:
points
,
newstate
=
self
.
simulate
(
moves
)
yield
newstate
.
score
(
points
,
moves
,
prev
),
moves
yield
newstate
.
score
(
points
,
moves
,
self
),
moves
except
AssertionError
:
pass
...
...
@@ -126,14 +145,11 @@ class State:
s
=
self
.
copy
()
points
=
0
# clear the current board before planning the next move
#s.score_points()
if
not
moves
:
return
s
.
score_points
(),
s
#if not moves:
# return s.score_points(), s
# avoid swapping/grabbing currently exploding items
unmoveable
=
s
.
find_unmovable_blocks
()
#
unmoveable = s.find_unmovable_blocks()
for
move
in
moves
:
if
move
==
LEFT
:
...
...
@@ -147,7 +163,7 @@ class State:
row
=
s
.
colskip
(
s
.
exa
)
assert
row
<
s
.
nrows
()
i
=
row
*
COLUMNS
+
s
.
exa
assert
i
not
in
unmoveable
#
assert i not in unmoveable
s
.
held
=
s
.
blocks
[
i
]
s
.
blocks
[
i
]
=
NOBLOCK
elif
move
==
DROP
:
...
...
@@ -157,20 +173,24 @@ class State:
i
=
row
*
COLUMNS
+
s
.
exa
s
.
blocks
[
i
-
COLUMNS
]
=
s
.
held
s
.
held
=
NOBLOCK
points
+=
s
.
score_points
()
#
points += s.score_points()
elif
move
==
SWAP
:
row
=
s
.
colskip
(
s
.
exa
)
assert
row
<
s
.
nrows
()
-
2
i
=
row
*
COLUMNS
+
s
.
exa
j
=
i
+
COLUMNS
assert
i
not
in
unmoveable
assert
j
not
in
unmoveable
#
assert i not in unmoveable
#
assert j not in unmoveable
s
.
blocks
[
i
],
s
.
blocks
[
j
]
=
s
.
blocks
[
j
],
s
.
blocks
[
i
]
points
+=
s
.
score_points
()
#points += s.score_points()
if
moves
and
max
(
self
.
colsizes
())
<
COLSIZE_MAX
:
assert
max
(
s
.
colsizes
())
<=
COLSIZE_MAX
points
+=
s
.
score_points
()
return
points
,
s
def
find_groups
(
self
,
depth
=
FIND_GROUPS_DEPTH
):
def
find_groups
(
self
,
depth
=
FIND_GROUPS_DEPTH
,
minsize
=
2
):
def
follow_group
(
i
,
block
,
group
):
if
self
.
blocks
[
i
]
==
block
and
i
not
in
visited
:
group
.
append
(
i
)
...
...
@@ -185,9 +205,7 @@ class State:
block
=
self
.
blocks
[
i
]
group
=
[]
follow_group
(
i
,
block
,
group
)
if
len
(
group
)
>
1
:
#for j in group:
# visited.add(j)
if
len
(
group
)
>=
minsize
:
yield
block
,
group
def
neighbors
(
self
,
i
):
...
...
@@ -208,31 +226,30 @@ class State:
def
fragmentation
(
self
,
depth
=
FRAG_DEPTH
):
"""
Sum the minimum distance from every
block i
n the first 3 layers to the
closest block of the same color
.
Minimize the sum of dist(i,j) for all
block
s
i
,j of the same color.
Prioritize horitontal distance to avoid column stacking
.
"""
def
find_closest
(
i
):
block
=
self
.
blocks
[
i
]
work
=
deque
([(
i
,
-
1
)])
visited
=
{
i
}
while
work
:
i
,
dist
=
work
.
popleft
()
if
dist
>=
0
and
self
.
blocks
[
i
]
==
block
:
return
dist
for
j
in
self
.
neighbors
(
i
):
if
j
not
in
visited
:
visited
.
add
(
j
)
work
.
append
((
j
,
dist
+
1
))
# only one of this type -> don't count as fragmented
return
0
return
sum
(
find_closest
(
i
)
for
col
in
self
.
iter_columns
()
for
i
in
islice
(
col
,
depth
))
def
dist
(
i
,
j
):
yi
,
xi
=
divmod
(
i
,
COLUMNS
)
yj
,
xj
=
divmod
(
j
,
COLUMNS
)
if
groups
[
i
]
==
groups
[
j
]:
return
abs
(
yj
-
yi
)
#return abs(xj - xi) * 2 + abs(yj - yi) - 1
return
abs
(
xj
-
xi
)
+
abs
(
yj
-
yi
)
*
2
-
1
colors
=
{}
groups
=
{}
groupsizes
=
{}
for
groupid
,
(
block
,
group
)
in
enumerate
(
self
.
find_groups
(
depth
,
1
)):
colors
.
setdefault
(
block
,
[]).
extend
(
group
)
for
i
in
group
:
groups
[
i
]
=
groupid
groupsizes
[
i
]
=
len
(
group
)
return
sum
(
dist
(
i
,
j
)
# * (1 + 2 * is_bomb(block))
for
block
,
color
in
colors
.
items
()
for
i
,
j
in
combinations
(
color
,
2
))
def
score_points
(
self
,
multiplier
=
1
):
remove
=
[]
...
...
@@ -243,7 +260,7 @@ class State:
remove
.
extend
(
group
)
points
+=
len
(
group
)
*
multiplier
elif
is_bomb
(
block
)
and
len
(
group
)
>=
MIN_BOMB_GROUP_SIZE
:
#
points +=
10
points
+=
BOMB_POINTS
remove
.
extend
(
group
)
for
i
,
other
in
enumerate
(
self
.
blocks
):
if
other
==
bomb_to_basic
(
block
):
...
...
@@ -262,24 +279,28 @@ class State:
points
+=
self
.
score_points
(
min
(
2
,
multiplier
*
2
))
return
points
def
has_explosion
(
self
):
return
any
(
is_bomb
(
block
)
and
any
(
self
.
blocks
[
j
]
==
block
for
j
in
self
.
neighbors
(
i
))
for
i
,
block
in
enumerate
(
self
.
blocks
))
def
gen_moves
(
self
):
yield
()
for
src
in
range
(
COLUMNS
):
diff
=
src
-
self
.
exa
def
make_move
(
diff
):
direction
=
RIGHT
if
diff
>
0
else
LEFT
mov1
=
abs
(
diff
)
*
(
direction
,)
return
abs
(
diff
)
*
(
direction
,)
for
src
in
range
(
COLUMNS
):
mov1
=
make_move
(
src
-
self
.
exa
)
yield
mov1
+
(
SWAP
,)
yield
mov1
+
(
GRAB
,
SWAP
,
DROP
)
yield
mov1
+
(
SWAP
,
GRAB
,
SWAP
,
DROP
)
for
dst
in
range
(
COLUMNS
):
diff
=
dst
-
src
direction
=
RIGHT
if
diff
>
0
else
LEFT
mov2
=
abs
(
diff
)
*
(
direction
,)
for
i
,
get
in
enumerate
(
GET
):
if
i
>
1
or
diff
!=
0
:
if
dst
!=
src
:
mov2
=
make_move
(
dst
-
src
)
for
get
in
GET
:
for
put
in
PUT
:
yield
mov1
+
get
+
mov2
+
put
...
...
@@ -287,7 +308,18 @@ class State:
if
self
.
held
!=
NOBLOCK
:
return
(
DROP
,)
if
self
.
nrows
()
<
MIN_ROWS
:
return
()
if
self
.
grabbing_of_dropping
():
return
()
if
self
.
has_explosion
():
return
()
score
,
moves
=
min
(
self
.
score_moves
())
if
not
moves
:
return
(
SPEED
,)
return
moves
def
print
(
self
):
...
...
@@ -299,7 +331,7 @@ class State:
def
moves_to_keys
(
moves
):
return
''
.
join
(
'
jjkad
'
[
move
]
for
move
in
moves
)
return
''
.
join
(
'
jjkad
l
'
[
move
]
for
move
in
moves
)
if
__name__
==
'
__main__
'
:
...
...
@@ -314,10 +346,13 @@ if __name__ == '__main__':
state
.
print
()
print
()
print
(
'
empty cols:
'
,
state
.
empty_column_score
())
print
()
start
=
time
.
time
()
moves
=
state
.
solve
()
print
(
'
moves:
'
,
moves_to_keys
(
moves
))
end
=
time
.
time
()
print
(
'
moves:
'
,
moves_to_keys
(
moves
))
print
(
'
elapsed:
'
,
end
-
start
)
print
()
...
...
@@ -326,9 +361,9 @@ if __name__ == '__main__':
newstate
.
print
()
print
()
#
for score, moves in sorted(state.score_moves()):
#
print('move %18s:' % moves_to_keys(moves), score)
#
#print('moves:', moves_to_keys(moves), moves)
#
#print('score:', score)
for
score
,
moves
in
sorted
(
state
.
score_moves
()):
print
(
'
move %18s:
'
%
moves_to_keys
(
moves
),
score
)
#print('moves:', moves_to_keys(moves), moves)
#print('score:', score)
#print('\nmoves:', moves_to_keys(state.solve()))
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment