Last active
April 15, 2023 00:49
-
-
Save shiracamus/9d51d8f7618ed60d9e77269f0f0107d4 to your computer and use it in GitHub Desktop.
CUI Othello
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Stone: | |
SPACE = "." | |
BLACK = "X" | |
WHITE = "O" | |
class Board: | |
def __init__(self, size): | |
self.cells = [[Stone.SPACE] * size for i in range(size)] | |
def __iter__(self): | |
return (tuple(row) for row in self.cells) | |
def __getitem__(self, pos): | |
x, y = pos | |
return self.cells[y][x] | |
def __setitem__(self, pos, stone): | |
x, y = pos | |
self.cells[y][x] = stone | |
def __contains__(self, pos): | |
x, y = pos | |
return 0 <= y < len(self.cells) and 0 <= x < len(self.cells[y]) | |
def is_space(self, x, y): | |
return self[x, y] == Stone.SPACE | |
class Action: | |
def __init__(self, board, stone, points): | |
self.board = board | |
self.stone = stone | |
self.points = points | |
def put(self): | |
for x, y in self.points: | |
self.board[x, y] = self.stone | |
class Othello: | |
DIRS = (-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1) | |
def __init__(self): | |
self.board = Board(size=8) | |
self.board[3, 3], self.board[3, 4] = Stone.WHITE, Stone.BLACK | |
self.board[4, 3], self.board[4, 4] = Stone.BLACK, Stone.WHITE | |
def __iter__(self): | |
return iter(self.board) | |
def reversibles(self, x, y, stone): | |
"""石を置いたときに反転できる座標、座標が一つでもあれば石を置ける""" | |
if not self.board.is_space(x, y): | |
return () # 空地でなければ座標なし | |
def one_direction(dx, dy): | |
points = [] # 8方向のうちの1方向での反転可能座標 | |
cx, cy = x + dx, y + dy | |
while (cx, cy) in self.board and not self.board.is_space(cx, cy): | |
if self.board[cx, cy] == stone: | |
# 自分のコマに辿り着いた、反転できるコマの座標を返す | |
return points | |
# 相手のコマ、自分のコマに辿り着いたときに反転できる | |
points.append((cx, cy)) | |
cx, cy = cx + dx, cy + dy | |
return [] # 盤の端に到達したか空地に到達したら反転できない | |
return tuple(sum((one_direction(dx, dy) for dx, dy in self.DIRS), [])) | |
def is_playable(self): | |
"""石を置ける場所があるならTrue""" | |
return any(self.reversibles(x, y, stone) | |
for y, row in enumerate(self) | |
for x, _ in enumerate(row) | |
for stone in (Stone.WHITE, Stone.BLACK)) | |
def actions(self, stone): | |
"""石を置ける座標と、置いたときの操作の辞書データ""" | |
return {(x, y): Action(self.board, stone, ((x, y),) + reversibles) | |
for y, row in enumerate(self) | |
for x, _ in enumerate(row) | |
for reversibles in [self.reversibles(x, y, stone)] | |
if reversibles} | |
def count(self, stone): | |
return sum(cell == stone for row in self for cell in row) | |
def winner(self): | |
black_stones = self.count(Stone.BLACK) | |
white_stones = self.count(Stone.WHITE) | |
return (Stone.BLACK if black_stones > white_stones else | |
Stone.WHITE if black_stones < white_stones else | |
None) | |
def human_cui(player, othello): | |
actions = othello.actions(player.stone) | |
if not actions: | |
print(f"{player}はコマを置けません。パスします。") | |
return | |
print() | |
print(f"{player}の番です。") | |
print("置ける場所(x y):", " , ".join(f"{x} {y}" for x, y in actions)) | |
while True: | |
try: | |
choice = input(f"コマ({player.stone})を置く位置 x y を指定してください >> ") | |
choice == "quit" and quit() # "quit"と入力されたらゲーム終了 | |
x, y = map(int, choice.split()) | |
if (x, y) in actions: | |
actions[x, y].put() # 選択場所と反転場所にコマを置く | |
return | |
print("その位置にはコマを置けません。") | |
except ValueError: | |
print("x座標とy座標の数値を空白を空けて入力してください。") | |
class Player: | |
def __init__(self, stone, name, strategy): | |
self.stone = stone | |
self.name = name | |
self.strategy = strategy | |
def __str__(self): | |
return f"{self.name}({self.stone})" | |
def play(self, othello): | |
self.strategy(self, othello) | |
class OthelloCUI: | |
def __init__(self, othello, player1, player2): | |
self.othello = othello | |
self.player1 = player1 | |
self.player2 = player2 | |
def play(self): | |
self.show_board() | |
player, opponent = self.player1, self.player2 | |
while self.othello.is_playable(): | |
player.play(self.othello) | |
player, opponent = opponent, player | |
self.show_board() | |
self.show_result() | |
def show_board(self): | |
othello, player1, player2 = self.othello, self.player1, self.player2 | |
print() | |
print(" 0 1 2 3 4 5 6 7") | |
for y, row in enumerate(othello): | |
print(f"{y}", *row) | |
print(f"{player1}のコマ数:", othello.count(player1.stone)) | |
print(f"{player2}のコマ数:", othello.count(player2.stone)) | |
print() | |
def show_result(self): | |
result = {self.player1.stone: f"{self.player1}の勝ち", | |
self.player2.stone: f"{self.player2}の勝ち", | |
None: "引き分け", | |
}[self.othello.winner()] | |
print("結果:", result) | |
def main(): | |
othello = Othello() | |
player1 = Player(Stone.BLACK, "プレーヤー1", human_cui) | |
player2 = Player(Stone.WHITE, "プレーヤー2", human_cui) | |
OthelloCUI(othello, player1, player2).play() | |
if __name__ == "__main__": | |
try: | |
main() | |
except KeyboardInterrupt: | |
print() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment