目次
config.py
#! /usr/bin/env python3
# config.py
# programmed by Saito-Saito-Saito
# explained in https://Saito-Saito-Saito.github.io/reversi
# last update: 23/12/2020
import sys
from logging import getLogger, StreamHandler, FileHandler, Formatter, DEBUG, INFO, WARNING, ERROR, CRITICAL
### LOG SETTINGS
DEFAULT_LOG_ADDRESS = 'log.txt'
DEFAULT_LOG_FORMAT = Formatter('%(asctime)s - %(levelname)s - logger:%(name)s - %(filename)s - L%(lineno)d - %(funcName)s - %(message)s')
# set up function
def setLogger(name='default', level=DEBUG, *, fhandler=None, fhandler_level=DEBUG, filename=DEFAULT_LOG_ADDRESS, filemode='w', fileformat=DEFAULT_LOG_FORMAT, shandler=None, shandler_level=CRITICAL, streamformat=DEFAULT_LOG_FORMAT):
logger = getLogger(name)
logger.setLevel(level)
# file handler
fhandler = fhandler or FileHandler(filename, mode=filemode)
fhandler.setLevel(fhandler_level)
fhandler.setFormatter(fileformat)
logger.addHandler(fhandler)
# stream handler
shandler = shandler or StreamHandler()
shandler.setLevel(shandler_level)
shandler.setFormatter(streamformat)
logger.addHandler(shandler)
return logger
logger = setLogger(__name__)
# board is 8 * 8
SIZE = 8
if int(SIZE / 2) != SIZE / 2:
logger.error('SIZE VALUE HAS TO BE EVEN')
print('SYSTEM ERROR')
sys.exit()
# rows & columns index
ROW = 0
COL = 1
# piece values
EMPTY = 0
B = BLACK = 1
W = WHITE = -1
# game proceeding/set
GAME_PRC = 0
GAME_SET = 1
# for return
SUCCEEDED = True
FAILED = False
# direction is represented as follows: [toROW - frROW, toCOL - frCOL]
WHOLE_DIRECTION = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1]
]
# whether the index is in the board
def InBoard(subject):
if 0 <= subject < SIZE:
return True
else:
return False
IO.py
#! /usr/bin/env python3
# IO.py
# programmed by Saito-Saito-Saito
# explained in https://Saito-Saito-Saito.github.io/reversi
# last update: 23/12/2020
import config
local_logger = config.setLogger(__name__)
# translate user's input into the index of the square
def InputFormat(s):
if len(s) != 2:
local_logger.info('len(s) == {}'.format(len(s)))
return config.FAILED
elif s[0].isdecimal() and config.InBoard(int(s[0]) - 1) and ord('a') <= ord(s[1]) <= ord('h'):
return [int(s[0]) - 1, ord(s[1]) - ord('a')]
elif s[1].isdecimal() and config.InBoard(int(s[1]) - 1) and ord('a') <= ord(s[0]) <= ord('h'):
return [int(s[1]) - 1, ord(s[0]) - ord('a')]
else:
local_logger.info('OUT OF FORMAT')
return config.FAILED
board.py
#! /usr/bin/env python3
# board.py
# programmed by Saito-Saito-Saito
# explained in https://Saito-Saito-Saito.github.io/reversi
# last update: 23/12/2020
import copy
from config import *
local_logger = setLogger(__name__)
class Board:
def __init__(self, input_board=[], *, status=GAME_PRC, winner=EMPTY, logger=None):
if len(input_board) == SIZE:
self.board = copy.deepcopy(input_board)
else:
self.board = []
for row in range(SIZE):
self.board.append([0 for col in range(SIZE)])
self.board[int(SIZE / 2) - 1][int(SIZE / 2) - 1] = WHITE
self.board[int(SIZE / 2) - 1][int(SIZE / 2)] = BLACK
self.board[int(SIZE / 2)][int(SIZE / 2) - 1] = BLACK
self.board[int(SIZE / 2)][int(SIZE / 2)] = WHITE
# 0:進行中(途中)PRC 1:決着SET
self.game_status = status
self.winner = winner
self.logger = logger or local_logger
def BoardPrint(self, logger=None):
logger = logger or self.logger
print('\n')
print('\t a b c d e f g h')
print('\t -------------------------------')
for row in range(SIZE):
print('\t{} |'.format(row + 1), end='')
for col in range(SIZE):
if self.board[row][col] == EMPTY:
print(' |', end='')
elif self.board[row][col] == WHITE:
print(' ● |', end='')
elif self.board[row][col] == BLACK:
print(' ○ |', end='')
else:
logger.critical('UNEXPECTED PLAYER VALUE in BoardPrint')
return False
print(' {}'.format(row + 1))
print('\t -------------------------------')
print('\t a b c d e f g h')
print('\n')
"""
turnjudge judges whether the piece can be turned
if player put a piece on [R, C] ...
1. check whether [R+direc[ROW], C+direc[COL]]==-player
2. if yes and turnjudge(player, R+direc..., C+direc..., direc)==True, you can turn the direction when you put a piece on [R,C]
3. if yes but turnjudge(player, R+direc..., C+direc..., direc)==False, you cannot turn the direction when you put a piece on [R, C] (it does not always mean that you cannot put a piece there)
"""
def turnjudge(self, player, row, col, direction: list, logger=None):
logger = logger or self.logger
# out of the board
if not (InBoard(row) and InBoard(col)):
logger.debug('{}, {}\tOUT OF THE BOARD'.format(row, col))
return False
# BLACK, WHITE or EMPTY
piece = self.board[row][col]
# EMPTY
if piece == EMPTY:
logger.debug('{}, {}\tREACHED TO EMPTY'.format(row, col))
return False
# OWN
elif piece == player:
logger.debug('{}, {}\tREACHED TO OWN PIEE'.format(row, col))
return True
# OPPONENT'S
elif piece == -player:
return self.turnjudge(player, row + direction[ROW], col + direction[COL], direction, logger)
# ERROR
else:
logger.error('UNEXPECTED VALUE of PLAYER in putjudge')
return False
def turn(self, player, row, col, logger=None):
logger = logger or local_logger
# out of the board
if not (InBoard(row) and InBoard(col)):
logger.info('OUT OF THE BOARD')
return FAILED
# there is already a piece
if self.board[row][col] != EMPTY:
logger.info('THERE IS ALREADY A PIECE')
return FAILED
turned = False
# searching all the direction for available one
for direction in WHOLE_DIRECTION:
focused = [row + direction[ROW], col + direction[COL]]
if not (InBoard(focused[ROW]) and InBoard(focused[COL])):
continue
next_piece = self.board[focused[ROW]][focused[COL]]
logger.debug('direc = {}, next_piece = {}'.format(direction, next_piece))
# in case available
if next_piece == -player and self.turnjudge(player, focused[ROW], focused[COL], direction):
while self.board[focused[ROW]][focused[COL]] == -player:
self.board[focused[ROW]][focused[COL]] = player
focused[ROW] += direction[ROW]
focused[COL] += direction[COL]
turned = True
# in case a piece was turned
if turned:
self.board[row][col] = player
return SUCCEEDED
# in case any piece was not turned
else:
logger.info('THERE IS NO DIRECTION AVAILABLE')
return FAILED
def passjudge(self, player, logger=None):
logger = logger or self.logger
# searching all EMPTY squares that can turn
for row in range(SIZE):
for col in range(SIZE):
if self.board[row][col] == EMPTY:
for direction in WHOLE_DIRECTION:
focused = [row + direction[ROW], col + direction[COL]]
if not (InBoard(focused[ROW]) and InBoard(focused[COL])):
continue
if self.board[focused[ROW]][focused[COL]] == -player and self.turnjudge(player, focused[ROW], focused[COL], direction):
logger.info('THERE IS {}, {}'.format(row, col))
return False
# completing all the loop, there is no square that you can put a piece
return True
def countpiece(self, logger=None):
logger = logger or self.logger
# count up all pieces
self.b_count = 0
self.w_count = 0
for row in range(SIZE):
for col in range(SIZE):
if self.board[row][col] == BLACK:
self.b_count += 1
elif self.board[row][col] == WHITE:
self.w_count += 1
return [self.b_count, self.w_count]
def gamesetjudge(self, logger=None):
logger = logger or self.logger
# if either can put, it's not gameset
if self.passjudge(BLACK) == False or self.passjudge(WHITE) == False:
logger.debug('either can put yet')
return False
else:
self.game_status = GAME_SET
return True
if __name__ == "__main__":
setup = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 0, 1, 0],
[0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, -1, 0, 1, 0]
]
TestBoard = Board(setup)
TestBoard.BoardPrint()
main.py
#! /usr/bin/env python3
# main.py
# programmed by Saito-Saito-Saito
# explained in https://Saito-Saito-Saito.github.io/reversi
# last update: 23/12/2020
from config import *
import IO
import board
# preset
logger = setLogger(__name__)
main_board = board.Board()
player = BLACK
main_board.BoardPrint()
while True:
### GAMESET JUDGE
# NOTE: you must not control game_status here because out of the loop it must be GAME_PRC
if main_board.gamesetjudge():
break
### PLAYER OUTPUT
if player == BLACK:
print('○ TURN', end=' ')
elif player == WHITE:
print('● TURN', end=' ')
else:
logger.error('UNEXPECTED VALUE of PLAYER in the while loop')
break
### PASS JUDGE
if main_board.passjudge(player):
print('BUT YOU CANNOT PUT ANYWHERE (PRESS ENTER TO PASS)')
input()
# player change
player *= -1
continue
### INPUT
print('(X to give up) >>> ', end='')
s = input()
# give up
if s in ['X', 'x']:
main_board.winner = -player
break
else:
square = IO.InputFormat(s)
logger.info('motion = {}'.format(square))
# invalid putting
if square == False:
print('INVALID INPUT')
continue
elif main_board.turn(player, *square) == FAILED:
print('INVALID PUTTING')
continue
### PLAYER CHANGE
player *= -1
main_board.BoardPrint()
print('\nGAME SET')
# in case of give up
if main_board.game_status == GAME_PRC:
print('INTERRUPTION')
if main_board.winner == BLACK:
print('BLACK WINS')
elif main_board.winner == WHITE:
print('WHITE WINS')
else:
print('SYSTEM ERROR: DRAW')
# the other cases of game set
else:
counter = main_board.countpiece()
print('{} - {}'.format(counter[0], counter[1]))
if counter[0] == counter[1]:
print('DRAW')
elif counter[0] > counter[1]:
print('○ WINS')
elif counter[0] < counter[1]:
print('● WINS')
else:
logger.error('ILLOGICAL ATTITUDE out of the while loop')
print('\nGAME OVER\n')