-
Notifications
You must be signed in to change notification settings - Fork 0
/
core.py
157 lines (124 loc) · 4.27 KB
/
core.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# -*- coding: utf-8 -*-
import re
from typing import List, Dict, Set, Any
import pygame
import config as cfg
from entities.coord import Coord
# Core (shared) data. Must be initialized invoking init()
# This global counter counts the total number of players instantiated (Pawns)
PAWNS = None
# Memoized put-wall cache
MEMOIZED_WALLS: Dict[str, Any] = {}
# --- Statistics ---
MEMOIZED_NODES = None # Memoized AI Nodes
MEMOIZED_NODES_HITS = None # Memoized Cache hits
# --- Game Board (Singleton)
BOARD = None
class CellArray:
""" Creates an array of the given value, with
the same size as the board.
"""
def __init__(self, board, value: any):
self.board = board
self.rows = board.rows
self.cols = board.cols
self.array: List[List[Any]] = [[value for _ in range(self.cols)] for _ in range(self.rows)]
def __getitem__(self, i: int) -> List[any]:
return self.array[i]
def get_cell(self, coord: Coord):
return self.array[coord.row][coord.col]
def set_cell(self, coord: Coord, value) -> None:
self.array[coord.row][coord.col] = value
class DistArray(CellArray):
""" An array which calculates minimum distances
for each get_cell.
"""
def __init__(self, pawn):
self.pawn = pawn
super().__init__(pawn.board, cfg.INF)
self.queue: Set[Coord] = set()
self.MEMOIZE_DISTANCES = {}
self.MEMO_HITS = 0
self.MEMO_COUNT = 0
self.stack = []
self.update()
def clean_memo(self):
""" Frees memory by removing unused states.
"""
l_ = 1 + len(self.board.pawns) * 4
k = self.board.state[l_:]
k = '.' * l_ + k.replace('1', '.') + '$'
r = re.compile(k)
for q in list(self.MEMOIZE_DISTANCES.keys()):
if not r.match(q):
del self.MEMOIZE_DISTANCES[q]
def update(self):
""" Computes minimum distances from the current
position to the goal.
"""
k = self.board.state
try:
self.array = self.MEMOIZE_DISTANCES[k]
self.MEMO_HITS += 1
return
except KeyError:
self.MEMO_COUNT += 1
for i in range(self.rows):
for j in range(self.cols):
self.array[i][j] = cfg.INF
for goal in self.pawn.goals:
self.set_cell(goal, 0) # Already in the goal
self.queue.add(goal)
self.update_distances()
self.MEMOIZE_DISTANCES[k] = self.array
def update_cell(self, coord: Coord):
""" Updates the get_cell if not locked yet.
"""
if coord in self.pawn.goals:
return
values = [self.get_cell(pos) for pos in self.pawn.valid_moves_from(coord)]
newval = 1 + min(values)
if newval < self.get_cell(coord):
self.set_cell(coord, newval)
self.queue.add(coord)
def update_distances(self):
cell = self.pawn.cell
self.pawn.cell = None
while self.queue:
coord = self.queue.pop()
for pos in self.pawn.valid_moves_from(coord):
self.update_cell(pos)
self.pawn.cell = cell
def draw(self):
""" Displays distance numbers in the screen
"""
for i in range(self.rows):
for j in range(self.cols):
r = self.board[i][j].rect
r.x = r.x + r.width - cfg.FONT_SIZE
r.y = r.y + r.height - cfg.FONT_SIZE
r.width = cfg.FONT_SIZE
r.height = cfg.FONT_SIZE
pygame.draw.rect(self.pawn.screen, cfg.FONT_BG_COLOR, r, 0) # Erases previous number
self.board.msg(r.x, r.y, str(self.array[i][j]))
def push_state(self):
self.stack.append(self.array)
super().__init__(self.pawn.board, cfg.INF)
def pop_state(self):
self.array = self.stack.pop()
@property
def shortest_path_len(self):
""" Return len of the shortest path
"""
return min([self.get_cell(pos) for pos in self.pawn.valid_moves])
def init():
global PAWNS
global MEMOIZED_WALLS
global MEMOIZED_NODES
global MEMOIZED_NODES_HITS
global BOARD
PAWNS = 0
MEMOIZED_WALLS = {}
MEMOIZED_NODES = 0
MEMOIZED_NODES_HITS = 0
BOARD = None