update directory structure

This commit is contained in:
2023-05-30 17:16:48 +08:00
parent 4968e6400e
commit 50111160e3
37 changed files with 150 additions and 159 deletions
+138
View File
@@ -0,0 +1,138 @@
# tag::scoring_imports[]
from __future__ import absolute_import
from collections import namedtuple
from game_logic.gotypes import Player, Point
# tag::scoring_territory[]
class Territory:
def __init__(self, territory_map): # <1>
self.num_black_territory = 0
self.num_white_territory = 0
self.num_black_stones = 0
self.num_white_stones = 0
self.num_dame = 0
self.dame_points = []
for point, status in territory_map.items(): # <2>
if status == Player.black:
self.num_black_stones += 1
elif status == Player.white:
self.num_white_stones += 1
elif status == 'territory_b':
self.num_black_territory += 1
elif status == 'territory_w':
self.num_white_territory += 1
elif status == 'dame':
self.num_dame += 1
self.dame_points.append(point)
# <1> A `territory_map` splits the board into stones, territory and neutral points (dame).
# <2> Depending on the status of a point, we increment the respective counter.
# end::scoring_territory[]
# tag::scoring_game_result[]
class GameResult(namedtuple('GameResult', 'b w komi')):
@property
def winner(self):
if self.b > self.w + self.komi:
return Player.black
return Player.white
@property
def winning_margin(self):
w = self.w + self.komi
return abs(self.b - w)
def __str__(self):
w = self.w + self.komi
if self.b > w:
return 'B+%.1f' % (self.b - w,)
return 'W+%.1f' % (w - self.b,)
# end::scoring_game_result[]
""" evaluate_territory:
Map a board into territory and dame.
Any points that are completely surrounded by a single color are
counted as territory; it makes no attempt to identify even
trivially dead groups.
"""
# tag::scoring_evaluate_territory[]
def evaluate_territory(board):
status = {}
for r in range(1, board.num_rows + 1):
for c in range(1, board.num_cols + 1):
p = Point(row=r, col=c)
if p in status: # <1>
continue
stone = board.get(p)
if stone is not None: # <2>
status[p] = board.get(p)
else:
group, neighbors = _collect_region(p, board)
if len(neighbors) == 1: # <3>
neighbor_stone = neighbors.pop()
stone_str = 'b' if neighbor_stone == Player.black else 'w'
fill_with = 'territory_' + stone_str
else:
fill_with = 'dame' # <4>
for pos in group:
status[pos] = fill_with
return Territory(status)
# <1> Skip the point, if you already visited this as part of a different group.
# <2> If the point is a stone, add it as status.
# <3> If a point is completely surrounded by black or white stones, count it as territory.
# <4> Otherwise the point has to be a neutral point, so we add it to dame.
# end::scoring_evaluate_territory[]
""" _collect_region:
Find the contiguous section of a board containing a point. Also
identify all the boundary points.
"""
# tag::scoring_collect_region[]
def _collect_region(start_pos, board, visited=None):
if visited is None:
visited = {}
if start_pos in visited:
return [], set()
all_points = [start_pos]
all_borders = set()
visited[start_pos] = True
here = board.get(start_pos)
deltas = [(-1, 0), (1, 0), (0, -1), (0, 1)]
for delta_r, delta_c in deltas:
next_p = Point(row=start_pos.row + delta_r, col=start_pos.col + delta_c)
if not board.is_on_grid(next_p):
continue
neighbor = board.get(next_p)
if neighbor == here:
points, borders = _collect_region(next_p, board, visited)
all_points += points
all_borders |= borders
else:
all_borders.add(neighbor)
return all_points, all_borders
# end::scoring_collect_region[]
# tag::scoring_compute_game_result[]
def compute_game_result(game_state):
territory = evaluate_territory(game_state.board)
return GameResult(
territory.num_black_territory + territory.num_black_stones,
territory.num_white_territory + territory.num_white_stones,
komi=7.5)
# end::scoring_compute_game_result[]