[Python] 黑白棋(翻轉棋)小遊戲

[Python] 黑白棋(翻轉棋)小遊戲

遊戲介紹

黑白棋(Reversi or Othello)在西方和日本很流行。遊戲經過相互翻轉對方的棋子,最後以棋盤上誰的棋子多來判斷勝負。python

規則

黑白棋的每顆棋子由黑白兩色組成,一面白,一面黑。每次落子,把本方顏色的棋子放在棋盤的空格上,若在橫、豎、斜八個方向的任一方向上有本方棋子,則被夾在中間的對手棋子所有翻轉爲本方棋子顏色;而且僅在能夠翻轉棋 子的地方纔能落子。若是一方至少有一步合法棋步可下,他就必須落子,不得棄權。git

棋盤已滿或雙方都沒有棋子可下時棋局結束,以棋子數目來計算勝負,棋子多的一方獲勝。在棋盤尚未下滿時,若是一方的棋子已經被對方吃光,則棋局也結束,將對手棋子吃光的一方獲勝。github

勝負斷定

兩位玩家輪流下棋,直到一方沒有符合規則的落子位置,在這種狀況下,剩下的一方 繼續下棋,直到對手有了能夠落子的位置。此時,恢復二者輪流下棋的順序。若是一方落子在非法位置,則視爲放棄本次對弈,對方獲勝。ui

遊戲結束的條件:spa

  1. 整個棋盤滿了
  2. 一方的棋子已經被對方吃光
  3. 兩名玩家都沒有能夠落子的棋盤格
  4. 一方落子在非法位置

前3種狀況以棋子數目來計算勝負,棋子多的一方獲勝;第四種狀況斷定對方獲勝。code

人機對弈流程

首先,程序詢問用戶棋盤的大小。接着,程序詢問用戶「計算機持黑棋仍是白棋」。orm

在本程序中,咱們用字母’X’表明黑棋, 用字母’O’表明白棋,而且假設老是黑棋玩家先走。blog

因此,若是計算機持黑棋,計算機就先走; 不然,程序提示人類玩家先走。遊戲

每走一步,程序輸出棋盤。黑白棋玩家輪流下棋,直到一個玩家無符合規則的落子位置。ip

此時,程序輸出信息「O player has no valid move.」(假設白棋玩家無棋可走),而且提示黑棋玩家繼續下棋。

每走一步,程序除輸出棋盤外,還要檢測遊戲是否結束。若是程序檢查出遊戲結束,輸出輸贏信息並停止程序。輸贏信息能夠是: 「O player wins.」, 「X player wins.」 或者「Draw!」. 若是用戶落子非法,程序應檢測到而且輸出「Invalid move.」, 結束程序,宣佈贏家。

計算機選擇落子位置的策略

對每一個可能的落子位置,都進行嘗試,計算該位置的「分值」(能夠翻轉的對手棋子數量),分值越高則在該位置落子越有利。計算每一個可能位置的分值,選擇最大值位置落子。須要注意的是:可能有2個或多個棋盤格有相同的分值。這種狀況下,選擇行字母最小的棋盤格。若是兩個棋盤格分值相同且在同一行,則選擇列字母較小的棋盤格。

程序執行

image.png截屏2020-05-22 20.00.26.png

代碼

完整代碼見
代碼倉庫

棋盤代碼

#!/usr/bin/python 
# -*- coding: utf-8 -*-

class Board():
    def __init__(self, n):
        self.n = n
        self.board = self.generateBoard()
        self.chess = {0: '.', 1: 'O', 2: 'X'}

    def generateBoard(self):
        # 0 empty 1 white 2 black
        i = int(self.n / 2)
        board = [[0] * self.n for _ in range(self.n)]
        board[i][i]=board[i-1][i-1] = 1
        board[i][i-1]=board[i-1][i] = 2
        return board

    def draw(self):
        index = 'abcdefghijklmnopqrstuvwxyz'
        print(' ',*index[:self.n])
        for h,row in zip(index,self.board):
            print(h,*map('.OX'.__getitem__,row))
        print()

遊戲邏輯

#!/usr/bin/python 
# -*- coding: utf-8 -*-

from board import Board
import itertools
import operator
import collections
from functools import reduce
from constant import Status

class Reversi():

    _DIRECTIONS = [(1,0),(1,1),(1,-1),(-1,0),(-1,1),(-1,-1),(0,1),(0,-1)]

    def __init__(self, n, turn):
        self.n = n                                              # board dimension
        self.b = Board(n)                                       # board
        self.turn = 0 if turn == 'X' or turn == 'x' else 1      # player turn
        self.step = 1                                           # game step
        self.status = Status.WAIT                               # game status

    def isValidPosition(self,x,y):
        return 0 <= x < self.n and 0 <= y < self.n
    
    def nextPosition(self,direction,x,y):
        x+=direction[0]
        y+=direction[1]
        return x,y
    
    def score(self,r,c):
        return list(itertools.chain.from_iterable([self.scoreDirection(r+m[0],c+m[1],m,self.step%2+1,[]) for m in Reversi._DIRECTIONS]))

    def scoreDirection(self,x,y,direction,color,turn):
        if not self.isValidPosition(x,y) or self.b.board[x][y]==0 :
            return []
        if self.b.board[x][y]!=color:
            turn+=[(x,y)]
            return self.scoreDirection(*self.nextPosition(direction,x,y),direction,color,turn)
        else:
            return turn

    def checkPut(self, pos):
        # check person put
        assert len(pos)>=2 , 'move position disable'
        r = ord(pos[0]) - 97
        c = ord(pos[1]) - 97
        assert 0 <= r < self.n and 0 <= c < self.n, 'move position disable'
        turnList = self.score(r, c)
        if turnList:
            # turn chess
            for x,y in turnList+[(r,c)]:
                self.b.board[x][y] = self.step % 2+1
            return True
        else:
            return False

    def checkGame(self):
        # check game status
        empty,oNum,xNum = operator.itemgetter(0,1,2)(collections.Counter(itertools.chain.from_iterable(self.b.board)))
        hasPut = True
        pos,turnList = self.aiPut()
        if not turnList:
            self.step += 1
            posNext,turnListNext = self.aiPut()
            if not turnListNext:
                hasPut = False
            else:
                self.step -= 1
                print('{} player has no valid move'.format(self.b.chess[self.step % 2+1]))
                self.step -= 1
                self.turn -= 1
                print('{} player go on'.format(self.b.chess[self.step % 2+1]))
        if empty ==0 or oNum==0 or xNum == 0 or not hasPut:
            self.status = [Status.DRAW.value,Status.OWIN.value,Status.XWIN.value][(oNum > xNum)-(oNum<xNum)]
    
    def cmp(self,a,b):
        if len(a[1])>len(b[1]):
            return a 
        elif len(a[1])==len(b[1]) and a[0]<b[0]:
            return a
        else:
            return b
            
    def aiPut(self):
        # computer put
        allPos = filter(lambda pos : self.b.board[pos[0]][pos[1]]==0,itertools.product(range(self.n),repeat=2))
        allScoreForPos  = map(lambda pos: [pos,self.score(pos[0],pos[1])],allPos)
        maxScorePos = reduce(self.cmp,allScoreForPos,[(),[]])
        return maxScorePos[0],maxScorePos[1]

    def aiPlay(self):
        pos,turnList = self.aiPut()
        if turnList:
            print('Computer places {} at {}'.format(self.b.chess[self.step % 2+1],chr(pos[0]+97)+chr(pos[1]+97)))
            for x,y in turnList+[pos]:
                self.b.board[x][y] = self.step % 2+1
            reversi.b.draw()
            self.step += 1
            self.turn += 1

    def pPlay(self):
        pos = input('Enter move for {} (RowCol):'.format(self.b.chess[self.step % 2+1]))
        if self.checkPut(pos):
            reversi.b.draw()
            self.step += 1
            self.turn += 1
        else:
            print('Invalid move')

    def play(self):
        self.status = Status.ONGOING
        plays = [self.aiPlay,self.pPlay]
        while self.status == Status.ONGOING:
            plays[self.turn % len(plays)]()
            self.checkGame()
        else:
            print('Game over. {}'.format(Status(self.status)))

if __name__ == "__main__":
    print('Enter the board dimension:')
    try:
        n = int(input())
    except Exception as e:
        print('the board dimension is invalid, start game with default dimension = 4')
        n = 4
    assert 4 <= n <= 26 and n % 2 == 0, 'the board dimension is disable'
    print('Computer plays (X/O):')
    turn = input()
    assert turn in ['X','x','O', 'o'], 'the symbol of computer is disable'
    # generate game
    reversi = Reversi(n, turn)
    # draw board
    reversi.b.draw()
    reversi.play()
    input('Enter to quit')
相關文章
相關標籤/搜索