Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
E
exapunks-hackmatch-bot
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Taddeüs Kroes
exapunks-hackmatch-bot
Commits
b62da615
Commit
b62da615
authored
Apr 06, 2020
by
Taddeüs Kroes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve fragmentation/columnsize scores, fiddle with params, comment some broken code
parent
295cfec6
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
118 additions
and
83 deletions
+118
-83
strategy.py
strategy.py
+118
-83
No files found.
strategy.py
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 in the first 3 layers to the
Minimize the sum of dist(i,j) for all blocks 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()))
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment