Quite a mess, but there's still more to be golfed. Explanation coming in a few hours.
The program uses scores 1, 3, 7. Input is a list of lists of chars representing each cell. To test the example board easily, we can do:
board = """
3 3 W . . . 4 . 4 . . 2 W . 4 . . 4 . 4
3 M W W . 1 1 . . 4 2 W . 3 C 4 4 . . 4
3 M 2 2 W 1 1 1 T 3 2 W 4 3 . 1 4 . 4 .
M M . W 2 2 . . . 2 2 W 3 . 1 1 1 . . .
. 4 M . W W 2 2 2 2 W W 3 . 1 4 . T . .
. . . . . W W W W W . 3 C 1 . . 2 2 2 2
. T 1 1 1 1 . . 2 . . 4 . . . 2 2 M M M
4 . W 4 . C 4 4 . . . . . . 2 M M M M M
. 4 W W . . . 4 M . . W . W . 2 2 2 M M
. . . . . . . M M . . W W . . . . 2 M .
. . . 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 . 1
M 3 3 . . . . . . . . 4 . T 2 . 2 4 1 .
M M . C . 4 . 4 . . . . . 1 2 4 2 1 1 .
M . . 1 . 4 . . . . M M 1 2 . . 2 1 . .
. . . W 1 1 4 1 1 . . . 1 2 . . 2 W W W
. . 1 1 W 1 T . 1 1 1 1 T . . 2 W . 4 .
. 1 1 W . 3 3 . . . . . . . . 2 W 4 C 3
C 1 3 3 3 . 3 . 4 . 4 . 4 . . 2 W 1 1 M
4 3 3 4 . M 4 3 . . . . . . . 2 W . . .
. . . 4 . M M 3 . . 4 4 . 4 . 2 W W . .
"""
board = [row.split() for row in board.strip().split("\n")]
print S(board)
# [52, 46, 43, 62]
Handling the hex grid
Since we are on a hex grid, we have to deal with neighbours a little differently. If we use a traditional 2D grid as our representation, then for (1, 1)
we have:
. N N . . . N N . . (0, 1), (0, 2) (-1, 0), (-1, 1)
N X N . . -> N X N . . -> Neighbours (1, 0), (1, 2) -> Offsets (0, -1), (0, 1)
. N N . . . N N . . (2, 1), (2, 2) (1, 0), (1, 1)
On closer inspection, we realise that the offsets depend on the parity of the row you're on. The above example is for odd rows, but on even rows the offsets are
(-1, -1), (-1, 0), (0, -1), (0, 1), (1, -1), (1, 0)
The only thing that has changed is that the 1st, 2nd, 5th and 6th pairs have had their second coordinate decremented by 1.
The lambda function N
takes a coordinate pair (row, col)
and returns all neighbours of the cell within the grid. The inner comprehension generates the above offsets by extracting them from a simple base 3 encoding, incrementing the second coordinate if the row is odd and adds the offsets to the cell in question to give the neighbours. The outer comprehension then filters, leaving just the neighbours that are within the bounds of the grid.
Ungolfed
def neighbours(row, col):
neighbour_set = set()
for dr, dc in {(-1,-1), (-1,0), (0,-1), (0,1), (1,-1), (1,0)}:
neighbour_set.add((row + dr, col + dc + (1 if dr != 0 and row%2 == 1 else 0)))
return {(r,c) for r,c in neighbour_set if 20>r>-1 and 20>c>-1}
def solve(board):
def flood_fill(char, row, col):
# Logic negated in golfed code to save a few bytes
is_char = (dummy[row][col] == char)
dummy[row][col] = "" if is_char else dummy[row][col]
if is_char:
for neighbour in neighbours(row, col):
flood_fill(char, *neighbour)
return is_char
def neighbour_check(row, col, char1, char2):
return board[row][col] == char1 and char2 in {board[r][c] for r,c in neighbours(row, col)}
dummy = [row[:] for row in board] # Need to deep copy for the flood fill
scores = [0]*4
for i,char in enumerate("1234"):
for row in range(20):
for col in range(20):
scores[i] += (char in board[row]) # Score 1
scores[i] += 20 * 3*neighbour_check(row, col, "C", char) # Core score
scores[i] += 20 * neighbour_check(row, col, char, "W") # Score 3
scores[i] += 20 * flood_fill(char, row, col) # Score 7
# Overcounted everything 20 times, divide out
scores[i] /= 20
return scores