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
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
import
time
from
collections
import
deque
from
itertools
import
combinations
,
islice
from
itertools
import
islice
from
parse
import
COLUMNS
,
NOBLOCK
,
detect_blocks
,
detect_exa
,
\
from
parser
import
COLUMNS
,
NOBLOCK
,
detect_blocks
,
detect_exa
,
\
detect_held
,
print_board
,
is_basic
,
is_bomb
,
bomb_to_basic
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
))
GET
=
((
GRAB
,),
(
SWAP
,
GRAB
),
(
GRAB
,
SWAP
,
DROP
,
SWAP
,
GRAB
))
PUT
=
((
DROP
,),
(
DROP
,
SWAP
),
(
DROP
,
SWAP
,
GRAB
,
SWAP
,
DROP
))
PUT
=
((
DROP
,),
(
DROP
,
SWAP
),
(
DROP
,
SWAP
,
GRAB
,
SWAP
,
DROP
))
#REVERSE = [DROP, GRAB, SWAP, RIGHT, LEFT]
MIN_BASIC_GROUP_SIZE
=
4
MIN_BASIC_GROUP_SIZE
=
4
MIN_BOMB_GROUP_SIZE
=
2
MIN_BOMB_GROUP_SIZE
=
2
FIND_GROUPS_DEPTH
=
20
FIND_GROUPS_DEPTH
=
4
FRAG_DEPTH
=
20
FRAG_DEPTH
=
4
COLSIZE_PRIO
=
5
COLSIZE_PRIO
=
5
COLSIZE_PRIO_HIGH
=
7
COLSIZE_MAX
=
8
BOMB_POINTS
=
2
MIN_ROWS
=
2
class
State
:
class
State
:
...
@@ -23,10 +25,12 @@ class State:
...
@@ -23,10 +25,12 @@ class State:
self
.
exa
=
exa
self
.
exa
=
exa
self
.
held
=
held
self
.
held
=
held
def
has_holes
(
self
):
def
grabbing_of_dropping
(
self
):
return
any
(
self
.
blocks
[
i
]
==
NOBLOCK
skip
=
self
.
colskip
(
self
.
exa
)
for
col
in
self
.
iter_columns
()
i
=
(
skip
+
1
)
*
COLUMNS
+
self
.
exa
for
i
in
col
)
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
):
def
iter_columns
(
self
):
nrows
=
self
.
nrows
()
nrows
=
self
.
nrows
()
...
@@ -54,45 +58,60 @@ class State:
...
@@ -54,45 +58,60 @@ class State:
for
col
in
range
(
COLUMNS
):
for
col
in
range
(
COLUMNS
):
yield
self
.
nrows
()
-
self
.
colskip
(
col
)
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
score
=
0
for
col
in
range
(
COLUMNS
):
for
col
in
range
(
COLUMNS
):
for
row
in
range
(
self
.
nrows
()):
for
row
in
range
(
skip
,
nrows
):
i
=
row
*
COLUMNS
+
col
if
self
.
blocks
[
row
*
COLUMNS
+
col
]
!=
NOBLOCK
:
if
self
.
blocks
[
i
]
!=
NOBLOCK
:
break
break
score
+=
row
+
1
score
+=
row
-
skip
+
1
return
score
return
score
def
score
(
self
,
points
,
moves
,
prev
):
def
score
(
self
,
points
,
moves
,
prev
):
frag
=
self
.
fragmentation
()
#colsizes = list(self.colsizes())
colsizes
=
list
(
self
.
colsizes
())
#mincol = min(colsizes)
#mincol = min(colsizes)
maxcol
=
max
(
colsizes
)
#
maxcol = max(colsizes)
colsize_score
=
maxcol
,
colsizes
.
count
(
maxcol
)
#, -mincol
#
colsize_score = maxcol, colsizes.count(maxcol) #, -mincol
#colsize_score = tuple(sorted(colsizes, reverse=True))
#colsize_score = tuple(sorted(colsizes, reverse=True))
#if prev.nrows() >= 6:
#if prev.nrows() >= 6:
# return colsize_score, -points, frag, len(moves)
# 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
:
frag
+=
colsize_score
return
colsize_score
,
frag
,
-
points
,
len
(
moves
)
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
:
else
:
return
-
points
,
frag
,
colsize_score
,
len
(
moves
)
return
-
points
,
frag
,
colsize_score
,
len
(
moves
)
def
score_moves
(
self
):
def
score_moves
(
self
):
# clear exploding blocks before computing colsize
# clear exploding blocks before computing colsize
prev
=
self
.
copy
()
#
prev = self.copy()
prev
.
score_points
()
#
prev.score_points()
for
moves
in
self
.
gen_moves
():
for
moves
in
self
.
gen_moves
():
try
:
try
:
points
,
newstate
=
self
.
simulate
(
moves
)
points
,
newstate
=
self
.
simulate
(
moves
)
yield
newstate
.
score
(
points
,
moves
,
prev
),
moves
yield
newstate
.
score
(
points
,
moves
,
self
),
moves
except
AssertionError
:
except
AssertionError
:
pass
pass
...
@@ -126,14 +145,11 @@ class State:
...
@@ -126,14 +145,11 @@ class State:
s
=
self
.
copy
()
s
=
self
.
copy
()
points
=
0
points
=
0
# clear the current board before planning the next move
#if not moves:
#s.score_points()
# return s.score_points(), s
if
not
moves
:
return
s
.
score_points
(),
s
# avoid swapping/grabbing currently exploding items
# avoid swapping/grabbing currently exploding items
unmoveable
=
s
.
find_unmovable_blocks
()
#
unmoveable = s.find_unmovable_blocks()
for
move
in
moves
:
for
move
in
moves
:
if
move
==
LEFT
:
if
move
==
LEFT
:
...
@@ -147,7 +163,7 @@ class State:
...
@@ -147,7 +163,7 @@ class State:
row
=
s
.
colskip
(
s
.
exa
)
row
=
s
.
colskip
(
s
.
exa
)
assert
row
<
s
.
nrows
()
assert
row
<
s
.
nrows
()
i
=
row
*
COLUMNS
+
s
.
exa
i
=
row
*
COLUMNS
+
s
.
exa
assert
i
not
in
unmoveable
#
assert i not in unmoveable
s
.
held
=
s
.
blocks
[
i
]
s
.
held
=
s
.
blocks
[
i
]
s
.
blocks
[
i
]
=
NOBLOCK
s
.
blocks
[
i
]
=
NOBLOCK
elif
move
==
DROP
:
elif
move
==
DROP
:
...
@@ -157,20 +173,24 @@ class State:
...
@@ -157,20 +173,24 @@ class State:
i
=
row
*
COLUMNS
+
s
.
exa
i
=
row
*
COLUMNS
+
s
.
exa
s
.
blocks
[
i
-
COLUMNS
]
=
s
.
held
s
.
blocks
[
i
-
COLUMNS
]
=
s
.
held
s
.
held
=
NOBLOCK
s
.
held
=
NOBLOCK
points
+=
s
.
score_points
()
#
points += s.score_points()
elif
move
==
SWAP
:
elif
move
==
SWAP
:
row
=
s
.
colskip
(
s
.
exa
)
row
=
s
.
colskip
(
s
.
exa
)
assert
row
<
s
.
nrows
()
-
2
assert
row
<
s
.
nrows
()
-
2
i
=
row
*
COLUMNS
+
s
.
exa
i
=
row
*
COLUMNS
+
s
.
exa
j
=
i
+
COLUMNS
j
=
i
+
COLUMNS
assert
i
not
in
unmoveable
#
assert i not in unmoveable
assert
j
not
in
unmoveable
#
assert j not in unmoveable
s
.
blocks
[
i
],
s
.
blocks
[
j
]
=
s
.
blocks
[
j
],
s
.
blocks
[
i
]
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
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
):
def
follow_group
(
i
,
block
,
group
):
if
self
.
blocks
[
i
]
==
block
and
i
not
in
visited
:
if
self
.
blocks
[
i
]
==
block
and
i
not
in
visited
:
group
.
append
(
i
)
group
.
append
(
i
)
...
@@ -185,9 +205,7 @@ class State:
...
@@ -185,9 +205,7 @@ class State:
block
=
self
.
blocks
[
i
]
block
=
self
.
blocks
[
i
]
group
=
[]
group
=
[]
follow_group
(
i
,
block
,
group
)
follow_group
(
i
,
block
,
group
)
if
len
(
group
)
>
1
:
if
len
(
group
)
>=
minsize
:
#for j in group:
# visited.add(j)
yield
block
,
group
yield
block
,
group
def
neighbors
(
self
,
i
):
def
neighbors
(
self
,
i
):
...
@@ -208,31 +226,30 @@ class State:
...
@@ -208,31 +226,30 @@ class State:
def
fragmentation
(
self
,
depth
=
FRAG_DEPTH
):
def
fragmentation
(
self
,
depth
=
FRAG_DEPTH
):
"""
"""
Sum the minimum distance from every
block i
n the first 3 layers to the
Minimize the sum of dist(i,j) for all
block
s
i
,j of the same color.
closest block of the same color
.
Prioritize horitontal distance to avoid column stacking
.
"""
"""
def
find_closest
(
i
):
def
dist
(
i
,
j
):
block
=
self
.
blocks
[
i
]
yi
,
xi
=
divmod
(
i
,
COLUMNS
)
work
=
deque
([(
i
,
-
1
)])
yj
,
xj
=
divmod
(
j
,
COLUMNS
)
visited
=
{
i
}
if
groups
[
i
]
==
groups
[
j
]:
return
abs
(
yj
-
yi
)
while
work
:
#return abs(xj - xi) * 2 + abs(yj - yi) - 1
i
,
dist
=
work
.
popleft
()
return
abs
(
xj
-
xi
)
+
abs
(
yj
-
yi
)
*
2
-
1
if
dist
>=
0
and
self
.
blocks
[
i
]
==
block
:
colors
=
{}
return
dist
groups
=
{}
groupsizes
=
{}
for
j
in
self
.
neighbors
(
i
):
if
j
not
in
visited
:
for
groupid
,
(
block
,
group
)
in
enumerate
(
self
.
find_groups
(
depth
,
1
)):
visited
.
add
(
j
)
colors
.
setdefault
(
block
,
[]).
extend
(
group
)
work
.
append
((
j
,
dist
+
1
))
for
i
in
group
:
groups
[
i
]
=
groupid
# only one of this type -> don't count as fragmented
groupsizes
[
i
]
=
len
(
group
)
return
0
return
sum
(
dist
(
i
,
j
)
# * (1 + 2 * is_bomb(block))
return
sum
(
find_closest
(
i
)
for
block
,
color
in
colors
.
items
()
for
col
in
self
.
iter_columns
()
for
i
,
j
in
combinations
(
color
,
2
))
for
i
in
islice
(
col
,
depth
))
def
score_points
(
self
,
multiplier
=
1
):
def
score_points
(
self
,
multiplier
=
1
):
remove
=
[]
remove
=
[]
...
@@ -243,7 +260,7 @@ class State:
...
@@ -243,7 +260,7 @@ class State:
remove
.
extend
(
group
)
remove
.
extend
(
group
)
points
+=
len
(
group
)
*
multiplier
points
+=
len
(
group
)
*
multiplier
elif
is_bomb
(
block
)
and
len
(
group
)
>=
MIN_BOMB_GROUP_SIZE
:
elif
is_bomb
(
block
)
and
len
(
group
)
>=
MIN_BOMB_GROUP_SIZE
:
#
points +=
10
points
+=
BOMB_POINTS
remove
.
extend
(
group
)
remove
.
extend
(
group
)
for
i
,
other
in
enumerate
(
self
.
blocks
):
for
i
,
other
in
enumerate
(
self
.
blocks
):
if
other
==
bomb_to_basic
(
block
):
if
other
==
bomb_to_basic
(
block
):
...
@@ -262,24 +279,28 @@ class State:
...
@@ -262,24 +279,28 @@ class State:
points
+=
self
.
score_points
(
min
(
2
,
multiplier
*
2
))
points
+=
self
.
score_points
(
min
(
2
,
multiplier
*
2
))
return
points
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
):
def
gen_moves
(
self
):
yield
()
yield
()
for
src
in
range
(
COLUMNS
):
def
make_move
(
diff
):
diff
=
src
-
self
.
exa
direction
=
RIGHT
if
diff
>
0
else
LEFT
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
+
(
SWAP
,)
yield
mov1
+
(
GRAB
,
SWAP
,
DROP
)
yield
mov1
+
(
GRAB
,
SWAP
,
DROP
)
yield
mov1
+
(
SWAP
,
GRAB
,
SWAP
,
DROP
)
for
dst
in
range
(
COLUMNS
):
for
dst
in
range
(
COLUMNS
):
diff
=
dst
-
src
if
dst
!=
src
:
direction
=
RIGHT
if
diff
>
0
else
LEFT
mov2
=
make_move
(
dst
-
src
)
mov2
=
abs
(
diff
)
*
(
direction
,)
for
get
in
GET
:
for
i
,
get
in
enumerate
(
GET
):
if
i
>
1
or
diff
!=
0
:
for
put
in
PUT
:
for
put
in
PUT
:
yield
mov1
+
get
+
mov2
+
put
yield
mov1
+
get
+
mov2
+
put
...
@@ -287,7 +308,18 @@ class State:
...
@@ -287,7 +308,18 @@ class State:
if
self
.
held
!=
NOBLOCK
:
if
self
.
held
!=
NOBLOCK
:
return
(
DROP
,)
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
())
score
,
moves
=
min
(
self
.
score_moves
())
if
not
moves
:
return
(
SPEED
,)
return
moves
return
moves
def
print
(
self
):
def
print
(
self
):
...
@@ -299,7 +331,7 @@ class State:
...
@@ -299,7 +331,7 @@ class State:
def
moves_to_keys
(
moves
):
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__
'
:
if
__name__
==
'
__main__
'
:
...
@@ -314,10 +346,13 @@ if __name__ == '__main__':
...
@@ -314,10 +346,13 @@ if __name__ == '__main__':
state
.
print
()
state
.
print
()
print
()
print
()
print
(
'
empty cols:
'
,
state
.
empty_column_score
())
print
()
start
=
time
.
time
()
start
=
time
.
time
()
moves
=
state
.
solve
()
moves
=
state
.
solve
()
print
(
'
moves:
'
,
moves_to_keys
(
moves
))
end
=
time
.
time
()
end
=
time
.
time
()
print
(
'
moves:
'
,
moves_to_keys
(
moves
))
print
(
'
elapsed:
'
,
end
-
start
)
print
(
'
elapsed:
'
,
end
-
start
)
print
()
print
()
...
@@ -326,9 +361,9 @@ if __name__ == '__main__':
...
@@ -326,9 +361,9 @@ if __name__ == '__main__':
newstate
.
print
()
newstate
.
print
()
print
()
print
()
#
for score, moves in sorted(state.score_moves()):
for
score
,
moves
in
sorted
(
state
.
score_moves
()):
#
print('move %18s:' % moves_to_keys(moves), score)
print
(
'
move %18s:
'
%
moves_to_keys
(
moves
),
score
)
#
#print('moves:', moves_to_keys(moves), moves)
#print('moves:', moves_to_keys(moves), moves)
#
#print('score:', score)
#print('score:', score)
#print('\nmoves:', moves_to_keys(state.solve()))
#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