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
0db87a73
Commit
0db87a73
authored
Apr 11, 2020
by
Taddeüs Kroes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Compute groups once and update them on each move
parent
2512a051
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
116 additions
and
103 deletions
+116
-103
strategy.py
strategy.py
+116
-103
No files found.
strategy.py
View file @
0db87a73
...
@@ -3,7 +3,7 @@ import time
...
@@ -3,7 +3,7 @@ import time
from
collections
import
deque
from
collections
import
deque
from
contextlib
import
redirect_stdout
from
contextlib
import
redirect_stdout
from
copy
import
copy
from
copy
import
copy
from
itertools
import
combinations
,
islice
from
itertools
import
combinations
from
detection
import
COLUMNS
,
NOBLOCK
,
detect_blocks
,
detect_exa
,
\
from
detection
import
COLUMNS
,
NOBLOCK
,
detect_blocks
,
detect_exa
,
\
detect_held
,
print_board
,
is_basic
,
is_bomb
detect_held
,
print_board
,
is_basic
,
is_bomb
...
@@ -20,25 +20,22 @@ MOVE_DELAYS = (
...
@@ -20,25 +20,22 @@ MOVE_DELAYS = (
)
)
MIN_BASIC_GROUP_SIZE
=
4
MIN_BASIC_GROUP_SIZE
=
4
MIN_BOMB_GROUP_SIZE
=
2
MIN_BOMB_GROUP_SIZE
=
2
POINTS_DEPTH
=
3
FRAG_DEPTH
=
4
DEFRAG_PRIO
=
4
COLSIZE_PRIO
=
5
COLSIZE_PRIO
=
5
COLSIZE_PANIC
=
8
COLSIZE_PANIC
=
8
COLSIZE_MAX
=
9
BOMB_POINTS
=
5
BOMB_POINTS
=
5
class
State
:
class
State
:
def
__init__
(
self
,
blocks
,
exa
,
held
,
colskip
,
busy
,
moves
,
placed
,
grabbed
):
def
__init__
(
self
,
blocks
,
exa
,
held
,
colskip
,
busy
,
moves
,
groups
,
groupsizes
,
maxgroup
):
self
.
blocks
=
blocks
self
.
blocks
=
blocks
self
.
exa
=
exa
self
.
exa
=
exa
self
.
held
=
held
self
.
held
=
held
self
.
colskip
=
colskip
self
.
colskip
=
colskip
self
.
busy
=
busy
self
.
busy
=
busy
self
.
moves
=
moves
self
.
moves
=
moves
self
.
placed
=
placed
self
.
groups
=
groups
self
.
grabbed
=
grabbed
self
.
groupsizes
=
groupsizes
self
.
maxgroup
=
maxgroup
self
.
nrows
=
len
(
blocks
)
//
COLUMNS
self
.
nrows
=
len
(
blocks
)
//
COLUMNS
@
classmethod
@
classmethod
...
@@ -48,7 +45,8 @@ class State:
...
@@ -48,7 +45,8 @@ class State:
held
=
detect_held
(
board
,
exa
)
held
=
detect_held
(
board
,
exa
)
colskip
=
get_colskip
(
blocks
)
colskip
=
get_colskip
(
blocks
)
busy
=
get_busy
(
blocks
,
colskip
)
busy
=
get_busy
(
blocks
,
colskip
)
return
cls
(
blocks
,
exa
,
held
,
colskip
,
busy
,
(),
set
(),
{})
groups
,
groupsizes
,
maxgroup
=
get_groups
(
blocks
)
return
cls
(
blocks
,
exa
,
held
,
colskip
,
busy
,
(),
groups
,
groupsizes
,
maxgroup
)
def
copy
(
self
,
deep
):
def
copy
(
self
,
deep
):
mcopy
=
copy
if
deep
else
lambda
x
:
x
mcopy
=
copy
if
deep
else
lambda
x
:
x
...
@@ -58,26 +56,18 @@ class State:
...
@@ -58,26 +56,18 @@ class State:
mcopy
(
self
.
colskip
),
mcopy
(
self
.
colskip
),
self
.
busy
,
self
.
busy
,
self
.
moves
,
self
.
moves
,
mcopy
(
self
.
placed
),
mcopy
(
self
.
groups
),
mcopy
(
self
.
grabbed
))
mcopy
(
self
.
groupsizes
),
self
.
maxgroup
))
def
colbusy
(
self
,
col
):
def
colbusy
(
self
,
col
):
return
(
self
.
busy
>>
col
)
&
1
return
(
self
.
busy
>>
col
)
&
1
def
grabbing_or_dropping
(
self
):
def
colrows
(
self
,
col
):
skip
=
self
.
colskip
[
self
.
exa
]
return
self
.
nrows
-
self
.
colskip
[
col
]
i
=
(
skip
+
1
)
*
COLUMNS
+
self
.
exa
return
i
<
len
(
self
.
blocks
)
and
self
.
blocks
[
i
]
==
NOBLOCK
def
iter_columns
(
self
):
def
gen_col
(
col
):
for
row
in
range
(
self
.
nrows
):
i
=
row
*
COLUMNS
+
col
if
self
.
blocks
[
i
]
!=
NOBLOCK
:
yield
i
for
col
in
range
(
COLUMNS
):
def
maxrows
(
self
):
yield
gen_col
(
col
)
return
max
(
map
(
self
.
colrows
,
range
(
COLUMNS
))
)
def
causes_panic
(
self
):
def
causes_panic
(
self
):
return
self
.
max_colsize
()
>=
COLSIZE_PANIC
return
self
.
max_colsize
()
>=
COLSIZE_PANIC
...
@@ -101,9 +91,6 @@ class State:
...
@@ -101,9 +91,6 @@ class State:
score
+=
row
-
start_row
+
1
score
+=
row
-
start_row
+
1
return
score
return
score
def
colrows
(
self
,
col
):
return
self
.
nrows
-
self
.
colskip
[
col
]
def
move
(
self
,
*
moves
):
def
move
(
self
,
*
moves
):
deep
=
any
(
move
in
(
GRAB
,
DROP
,
SWAP
)
for
move
in
moves
)
deep
=
any
(
move
in
(
GRAB
,
DROP
,
SWAP
)
for
move
in
moves
)
s
=
self
.
copy
(
deep
)
s
=
self
.
copy
(
deep
)
...
@@ -124,22 +111,21 @@ class State:
...
@@ -124,22 +111,21 @@ 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
s
.
grabbed
[
i
]
=
s
.
held
=
s
.
blocks
[
i
]
s
.
held
=
s
.
blocks
[
i
]
s
.
blocks
[
i
]
=
NOBLOCK
s
.
blocks
[
i
]
=
NOBLOCK
s
.
grabbed
[
i
]
=
s
.
held
s
.
colskip
[
s
.
exa
]
+=
1
s
.
colskip
[
s
.
exa
]
+=
1
s
.
ungroup
(
i
)
elif
move
==
DROP
:
elif
move
==
DROP
:
assert
not
s
.
colbusy
(
s
.
exa
)
assert
not
s
.
colbusy
(
s
.
exa
)
assert
s
.
held
!=
NOBLOCK
assert
s
.
held
!=
NOBLOCK
row
=
s
.
colskip
[
s
.
exa
]
row
=
s
.
colskip
[
s
.
exa
]
assert
row
>
0
assert
row
>
0
# XXX assert s.nrows - row < COLSIZE_MAX
i
=
(
row
-
1
)
*
COLUMNS
+
s
.
exa
i
=
(
row
-
1
)
*
COLUMNS
+
s
.
exa
s
.
blocks
[
i
]
=
s
.
held
s
.
blocks
[
i
]
=
s
.
held
s
.
held
=
NOBLOCK
s
.
held
=
NOBLOCK
s
.
placed
.
add
(
i
)
s
.
colskip
[
s
.
exa
]
-=
1
s
.
colskip
[
s
.
exa
]
-=
1
s
.
regroup
(
i
)
elif
move
==
SWAP
:
elif
move
==
SWAP
:
assert
not
s
.
colbusy
(
s
.
exa
)
assert
not
s
.
colbusy
(
s
.
exa
)
...
@@ -150,45 +136,55 @@ class State:
...
@@ -150,45 +136,55 @@ class State:
bi
=
s
.
blocks
[
i
]
bi
=
s
.
blocks
[
i
]
bj
=
s
.
blocks
[
j
]
bj
=
s
.
blocks
[
j
]
if
bi
!=
bj
:
if
bi
!=
bj
:
s
.
blocks
[
i
]
=
bj
s
.
blocks
[
i
]
=
NOBLOCK
s
.
blocks
[
j
]
=
NOBLOCK
s
.
ungroup
(
i
)
s
.
ungroup
(
j
)
s
.
blocks
[
j
]
=
bi
s
.
blocks
[
j
]
=
bi
s
.
grabbed
[
i
]
=
bi
s
.
regroup
(
j
)
s
.
grabbed
[
j
]
=
bj
s
.
blocks
[
i
]
=
bj
s
.
placed
.
add
(
i
)
s
.
regroup
(
i
)
s
.
placed
.
add
(
j
)
return
s
return
s
def
find_groups
(
self
,
depth
=
POINTS_DEPTH
,
minsize
=
2
):
def
ungroup
(
self
,
i
):
def
follow_group
(
i
,
block
,
group
):
assert
self
.
blocks
[
i
]
==
NOBLOCK
if
self
.
blocks
[
i
]
==
block
and
i
not
in
visited
:
visited
=
set
()
group
.
append
(
i
)
oldid
=
self
.
groups
[
i
]
for
nb
in
neighbors
(
i
,
self
.
blocks
):
if
self
.
groups
[
nb
]
==
oldid
:
newgroup
=
self
.
scan_group
(
nb
,
visited
)
if
newgroup
:
self
.
maxgroup
=
newid
=
self
.
maxgroup
+
1
for
j
in
newgroup
:
self
.
groups
[
j
]
=
newid
self
.
groupsizes
[
j
]
=
len
(
newgroup
)
self
.
groups
[
i
]
=
0
self
.
groupsizes
[
i
]
=
0
def
regroup
(
self
,
i
):
assert
self
.
blocks
[
i
]
!=
NOBLOCK
self
.
maxgroup
=
newid
=
self
.
maxgroup
+
1
newgroup
=
self
.
scan_group
(
i
,
set
())
for
j
in
newgroup
:
self
.
groups
[
j
]
=
newid
self
.
groupsizes
[
j
]
=
len
(
newgroup
)
def
scan_group
(
self
,
start
,
visited
):
def
scan
(
i
):
if
i
not
in
visited
:
yield
i
visited
.
add
(
i
)
visited
.
add
(
i
)
for
nb
in
self
.
neighbors
(
i
):
for
nb
in
neighbors
(
i
,
self
.
blocks
):
follow_group
(
nb
,
block
,
group
)
if
self
.
blocks
[
nb
]
==
block
:
yield
from
scan
(
nb
)
visited
=
set
()
block
=
self
.
blocks
[
start
]
return
tuple
(
scan
(
start
))
for
col
in
self
.
iter_columns
():
def
fragmentation
(
self
):
for
i
in
islice
(
col
,
depth
):
block
=
self
.
blocks
[
i
]
group
=
[]
follow_group
(
i
,
block
,
group
)
if
len
(
group
)
>=
minsize
:
yield
block
,
group
def
neighbors
(
self
,
i
):
row
,
col
=
divmod
(
i
,
COLUMNS
)
if
col
>
0
and
self
.
blocks
[
i
-
1
]
!=
NOBLOCK
:
yield
i
-
1
if
col
<
COLUMNS
-
1
and
self
.
blocks
[
i
+
1
]
!=
NOBLOCK
:
yield
i
+
1
if
row
>
0
and
self
.
blocks
[
i
-
COLUMNS
]
!=
NOBLOCK
:
yield
i
-
COLUMNS
if
row
<
self
.
nrows
-
1
and
self
.
blocks
[
i
+
COLUMNS
]
!=
NOBLOCK
:
yield
i
+
COLUMNS
def
fragmentation
(
self
,
depth
=
FRAG_DEPTH
):
"""
"""
Minimize the sum of dist(i,j) between all blocks i,j of the same color.
Minimize the sum of dist(i,j) between all blocks i,j of the same color.
Magnify vertical distances to avoid column stacking.
Magnify vertical distances to avoid column stacking.
...
@@ -199,64 +195,45 @@ class State:
...
@@ -199,64 +195,45 @@ class State:
# for blocks in the same group, only count vertical distance so
# for blocks in the same group, only count vertical distance so
# that groups are spread out horizontally
# that groups are spread out horizontally
if
groups
[
i
]
==
groups
[
j
]:
if
self
.
groups
[
i
]
==
self
.
groups
[
j
]:
return
abs
(
yj
-
yi
)
return
abs
(
yj
-
yi
)
return
abs
(
xj
-
xi
)
+
abs
(
yj
-
yi
)
*
2
-
1
return
abs
(
xj
-
xi
)
+
abs
(
yj
-
yi
)
*
2
-
1
colors
=
{}
colors
=
{}
groups
=
{}
for
i
,
block
in
enumerate
(
self
.
blocks
):
if
block
!=
NOBLOCK
:
for
groupid
,
(
block
,
group
)
in
enumerate
(
self
.
find_groups
(
depth
,
1
)):
colors
.
setdefault
(
block
,
[]).
append
(
i
)
colors
.
setdefault
(
block
,
[]).
extend
(
group
)
for
i
in
group
:
groups
[
i
]
=
groupid
return
sum
(
dist
(
i
,
j
)
return
sum
(
dist
(
i
,
j
)
for
block
,
color
in
colors
.
item
s
()
for
block
s
in
colors
.
value
s
()
for
i
,
j
in
combinations
(
color
,
2
))
for
i
,
j
in
combinations
(
blocks
,
2
))
def
points
(
self
):
def
group_leaders
(
self
):
def
group_size
(
start
):
seen
=
set
()
work
=
[
start
]
for
i
,
groupid
in
enumerate
(
self
.
groups
):
visited
.
add
(
start
)
if
groupid
>
0
and
groupid
not
in
seen
:
size
=
0
seen
.
add
(
groupid
)
block
=
self
.
blocks
[
start
]
yield
i
while
work
:
i
=
work
.
pop
()
# avoid giving points to moving a block within the same group
if
self
.
grabbed
.
get
(
i
,
None
)
==
block
:
return
0
if
self
.
blocks
[
i
]
==
block
:
size
+=
1
for
nb
in
self
.
neighbors
(
i
):
if
nb
not
in
visited
:
visited
.
add
(
nb
)
work
.
append
(
nb
)
return
size
def
points
(
self
):
points
=
0
points
=
0
visited
=
set
()
for
i
in
self
.
placed
:
for
leader
in
self
.
group_leaders
():
if
i
not
in
visited
:
block
=
self
.
blocks
[
leader
]
block
=
self
.
blocks
[
i
]
size
=
self
.
groupsizes
[
leader
]
size
=
group_size
(
i
)
if
is_basic
(
block
)
and
size
>=
MIN_BASIC_GROUP_SIZE
:
if
is_basic
(
block
)
and
size
>=
MIN_BASIC_GROUP_SIZE
:
points
+=
size
points
+=
size
elif
is_bomb
(
block
)
and
size
>=
MIN_BOMB_GROUP_SIZE
:
elif
is_bomb
(
block
)
and
size
>=
MIN_BOMB_GROUP_SIZE
:
points
+=
BOMB_POINTS
points
+=
self
.
maxrows
()
return
-
points
return
-
points
def
gen_moves
(
self
):
def
gen_moves
(
self
):
yield
self
yield
self
for
src
in
self
.
gen_shift
(
not
self
.
grabbing_or_dropping
(
)):
for
src
in
self
.
gen_shift
(
not
self
.
colbusy
(
self
.
exa
)):
yield
from
src
.
gen_stationary
()
yield
from
src
.
gen_stationary
()
for
get
in
src
.
gen_get
():
for
get
in
src
.
gen_get
():
...
@@ -421,6 +398,42 @@ def get_busy(blocks, colskip):
...
@@ -421,6 +398,42 @@ def get_busy(blocks, colskip):
return
mask
return
mask
def
scan_group
(
blocks
,
i
,
block
,
visited
):
yield
i
visited
.
add
(
i
)
for
nb
in
neighbors
(
i
,
blocks
):
if
blocks
[
nb
]
==
block
and
nb
not
in
visited
:
yield
from
scan_group
(
blocks
,
nb
,
block
,
visited
)
def
get_groups
(
blocks
):
groupid
=
0
groups
=
[
0
]
*
len
(
blocks
)
groupsizes
=
[
0
]
*
len
(
blocks
)
visited
=
set
()
for
i
,
block
in
enumerate
(
blocks
):
if
block
!=
NOBLOCK
and
i
not
in
visited
:
groupid
+=
1
group
=
tuple
(
scan_group
(
blocks
,
i
,
block
,
visited
))
for
j
in
group
:
groups
[
j
]
=
groupid
return
groups
,
groupsizes
,
groupid
def
neighbors
(
i
,
blocks
):
y
,
x
=
divmod
(
i
,
COLUMNS
)
if
x
>
0
:
yield
i
-
1
if
x
<
COLUMNS
-
1
:
yield
i
+
1
if
y
>
0
:
yield
i
-
COLUMNS
if
y
<
len
(
blocks
)
//
COLUMNS
-
1
:
yield
i
+
COLUMNS
def
move_to_key
(
move
):
def
move_to_key
(
move
):
return
'jjkadl'
[
move
]
return
'jjkadl'
[
move
]
...
...
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