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