Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support #81, #83, #84, Fix #82 #87

Merged
merged 6 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 78 additions & 1 deletion board/go_board.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""碁盤のデータ定義と操作処理。
"""
from typing import List, NoReturn
from typing import List, Tuple, NoReturn
from collections import deque
import numpy as np

Expand Down Expand Up @@ -184,6 +184,56 @@ def put_stone(self, pos: int, color: Stone) -> NoReturn:
self.record.save(self.moves, color, pos, self.positional_hash)
self.moves += 1

def put_handicap_stone(self, pos: int, color: Stone) -> NoReturn:
"""指定された座標に指定された色の置き石を置く。

Args:
pos (int): 石を置く座標。
color (Stone): 置く石の色。
"""
opponent_color = Stone.get_opponent_color(color)

self.board[pos] = color
self.pattern.put_stone(pos, color)
self.positional_hash = affect_stone_hash(self.positional_hash, pos, color)

neighbor4 = self.get_neighbor4(pos)

connection = []
prisoner = 0

for neighbor in neighbor4:
if self.board[neighbor] == color:
self.strings.remove_liberty(neighbor, pos)
connection.append(self.strings.get_id(neighbor))
elif self.board[neighbor] == opponent_color:
self.strings.remove_liberty(neighbor, pos)
if self.strings.get_num_liberties(neighbor) == 0:
removed_stones = self.strings.remove_string(self.board, neighbor)
prisoner += len(removed_stones)
for removed_pos in removed_stones:
self.pattern.remove_stone(removed_pos)
self.positional_hash = affect_string_hash(self.positional_hash, \
removed_stones, opponent_color)

if color == Stone.BLACK:
self.prisoner[0] += prisoner
elif color == Stone.WHITE:
self.prisoner[1] += prisoner

if len(connection) == 0:
self.strings.make_string(self.board, pos, color)
if prisoner == 1 and self.strings.get_num_liberties(pos) == 1:
self.ko_move = self.moves
self.ko_pos = self.strings.string[self.strings.get_id(pos)].lib[0]
elif len(connection) == 1:
self.strings.add_stone(self.board, pos, color, connection[0])
else:
self.strings.connect_string(self.board, pos, color, connection)

# 着手した時に記録
self.record.save_handicap(pos)

def _is_suicide(self, pos: int, color: Stone) -> bool:
"""自殺手か否かを判定する。
自殺手ならTrue、そうでなければFalseを返す。
Expand Down Expand Up @@ -469,6 +519,33 @@ def get_komi(self) -> float:
"""
return self.komi

def get_to_move(self) -> Stone:
"""手番の色を取得する。

Returns:
Stone: 手番の色。
"""
if self.moves == 1:
return Stone.BLACK
last_move_color, _, _ = self.record.get(self.moves - 1)
return Stone.get_opponent_color(last_move_color)

def get_move_history(self) -> List[Tuple[Stone, int, np.array]]:
"""着手の履歴を取得する。

Returns:
[(Stone, int, np.array), ...]: (着手の色、座標、ハッシュ値) のリスト。
"""
return [self.record.get(m) for m in range(1, self.moves)]

def get_handicap_history(self) -> List[int]:
"""置き石の座標を取得する。

Returns:
List[int]: 置き石の座標のリスト。
"""
return self.record.handicap_pos[:]

def count_score(self) -> int: # pylint: disable=R0912
"""領地を簡易的にカウントする。

Expand Down
83 changes: 83 additions & 0 deletions board/handicap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""置き石の座標。
"""
from typing import List


handicap_coordinate_map = {
9 : {
2 : ["G7", "C3",],
3 : ["C7", "G7", "C3"],
4 : ["C7", "G7", "C3", "G3"],
5 : ["C7", "G7", "E5", "C3", "G3"],
6 : ["C7", "G7", "C5", "G5", "C3", "G3"],
7 : ["C7", "G7", "C5", "E5", "G5", "C3", "G3"],
8 : ["C7", "E7", "G7", "C5", "G5", "C3", "E3", "G3"],
9 : ["C7", "E7", "G7", "C5", "E5", "G5", "C3", "E3", "G3"],
},
11 : {
2 : ["J9", "C3"],
3 : ["C9", "J9", "C3"],
4 : ["C9", "J9", "C3", "J3"],
5 : ["C9", "J9", "F6", "C3", "J3"],
6 : ["C9", "J9", "C6", "J6", "C3", "J3"],
7 : ["C9", "J9", "C6", "F6", "J6", "C3", "J3"],
8 : ["C9", "F9", "J9", "C6", "J6", "C3", "F3", "J3"],
9 : ["C9", "F9", "J9", "C6", "F6", "J6", "C3", "F3", "J3"],
},
13 : {
2 : ["K10", "D4"],
3 : ["D10", "K10", "D4"],
4 : ["D10", "K10", "D4", "K4"],
5 : ["D10", "K10", "G7", "D4", "K4"],
6 : ["D10", "K10", "D7", "K7", "D4", "K4"],
7 : ["D10", "K10", "D7", "G7", "K7", "D4", "K4"],
8 : ["D10", "G10", "K10", "D7", "K7", "D4", "G4", "K4"],
9 : ["D10", "G10", "K10", "D7", "G7", "K7", "D4", "G4", "K4"],
},
15 : {
2 : ["M12", "D4"],
3 : ["D12", "M12", "D4"],
4 : ["D12", "M12", "D4", "M4"],
5 : ["D12", "M12", "H8", "D4", "M4"],
6 : ["D12", "M12", "D8", "M8", "D4", "M4"],
7 : ["D12", "M12", "D8", "H8", "M8", "D4", "M4"],
8 : ["D12", "H12", "M12", "D8", "M8", "D4", "H4", "M4"],
9 : ["D12", "H12", "M12", "D8", "H8", "M8", "D4", "H4", "M4"],
},
17 : {
2 : ["O14", "D4"],
3 : ["D14", "O14", "D4"],
4 : ["D14", "O14", "D4", "O4"],
5 : ["D14", "O14", "J9", "D4", "O4"],
6 : ["D14", "O14", "D9", "O9", "D4", "O4"],
7 : ["D14", "O14", "D9", "J9", "O9", "D4", "O4"],
8 : ["D14", "J14", "O14", "D9", "O9", "D4", "J4", "O4"],
9 : ["D14", "J14", "O14", "D9", "J9", "O9", "D4", "J4", "O4"],
},
19 : {
2 : ["Q16", "D4"],
3 : ["D16", "Q16", "D4"],
4 : ["D16", "Q16", "D4", "Q4"],
5 : ["D16", "Q16", "K10", "D4", "Q4"],
6 : ["D16", "Q16", "D10", "Q10", "D4", "Q4"],
7 : ["D16", "Q16", "D10", "K10", "Q10", "D4", "Q4"],
8 : ["D16", "K16", "Q16", "D10", "Q10", "D4", "K4", "Q4"],
9 : ["D16", "K16", "Q16", "D10", "K10", "Q10", "D4", "K4", "Q4"],
},
}


def get_handicap_coordinates(size: int, handicaps: int) -> List[int]:
"""置き石の座標リストを取得する。

Args:
size (int): 碁盤のサイズ。
handicaps (int): 置き石の数。

Returns:
List[int]: 置き石の座標リスト。
"""
if size in handicap_coordinate_map and \
handicaps in handicap_coordinate_map[size]:
return handicap_coordinate_map[size][handicaps]
return None
10 changes: 10 additions & 0 deletions board/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ def __init__(self):
self.color = [Stone.EMPTY] * MAX_RECORDS
self.pos = [PASS] * MAX_RECORDS
self.hash_value = np.zeros(shape=MAX_RECORDS, dtype=np.uint64)
self.handicap_pos = []

def clear(self) -> NoReturn:
"""データを初期化する。
"""
self.color = [Stone.EMPTY] * MAX_RECORDS
self.pos = [PASS] * MAX_RECORDS
self.hash_value.fill(0)
self.handicap_pos = []

def save(self, moves: int, color: Stone, pos: int, hash_value: np.array) -> NoReturn:
"""着手の履歴の記録する。
Expand All @@ -41,6 +43,14 @@ def save(self, moves: int, color: Stone, pos: int, hash_value: np.array) -> NoRe
else:
print_err("Cannot save move record.")

def save_handicap(self, pos: int) -> NoReturn:
"""置き石の座標を記録する。

Args:
pos (int): 置き石の座標。
"""
self.handicap_pos.append(pos)

def has_same_hash(self, hash_value: np.array) -> bool:
"""同じハッシュ値があるかを確認する。

Expand Down
Loading