Notice
Recent Posts
Recent Comments
Today
Total
05-19 09:04
Archives
관리 메뉴

Jeongchul Kim

ReversiGame.py 본문

Computer Language

ReversiGame.py

김 정출 2015. 1. 16. 18:13

오델로, 리버시, Reversi게임

  1. # Reversi game
  2.  
  3. # 리버시 게임 오델로 라고도 한다.
  4. # 가로8, 세로8로 도하 64개의 게임판 위에 흰색과 검은색 돌을 번갈아 놓는게임
  5. # 게임종료 후 판 위에 내 돌이 더 많이 있으면 승리하게 됨
  6. # 두 플레이어 모두 더 이상 상대방의 막대를 뒤집을 수 없을때 게임이 종료됨
  7. # 상대의 수를 먼저 읽는것이 유리한 바둑과 유사한 2인용 게임
  8. # 게임판 위의 네 모서리를 먼저 장악하는것이 승리요소임
  9. # 돌을 두었을 때, 상하좌우 혹은 대각선 방향으로 같은 색의 돌이 있으면,
  10. # 그 사이에 있는 상대방의 돌이 모두 자신의 돌로 바뀐다.
  11.  
  12. # drawBoard(board) : 넘겨받은 보드를 출력한다.
  13. # resetBoard(board) : 넘겨 받은 보드를 시작 위치를 제외하고 리셋한다.
  14. # getNewBoard() : 비어있는 새 board 데이터 구조를 만든다.
  15. # isValidMove(board,tile,xstart,ystart) : 플레이어가 놓인 위치가 적절한 위치가 아니면 False를 반혼다.
  16. #    만약 적절한 위치면 플레이어가 놓은 위치에 따라 플레이어의 타일이 된 영역을 모두 리스트로 만들어 반환한다.
  17. # isOnBoard(x,y) : 보드에 좌표가 위치하면 True를 반환
  18. # getBoardWithValidMoves(board,tile) : 플레이어가 놓을 수 잇는 유효 위치에 '.'를 표시해 board를 반환한다.
  19. # getScoreOfBoard(board) : 타일을 세서 점수를 계산한다. 'X','O'로 된 딕셔너리를 반환한다.
  20. # enterPlayerTile() : 플레이어가 어떤 타일로 게임을 할 것인지 선택하도록 한다.
  21. # whoGoesFirst() : 누가 먼저 플레이할 것인지 임의로 정한다.
  22. # playAgain() : 플레이어가 재게임 진행 시 True반환, 아니면 False
  23. # makeMove(board,tile,xstart,ystart) : 보드에 xstart,ystart 위치에 타일을 놓고 상대편의 타일을 뒤집는다.
  24. # getBoardCopy(board) : 보드의 복사본을 만들어 복사본 반환
  25. # isonCorner(x,y) : 4개의 코너 중 하나이면 True반환
  26. # getPlayerMove(board,playerTile) : 플레이어에게 위치를 입력하여 [x,y]를 반환
  27. # getComputerMove(board,computerTile) : board와 computerTile을 받아 어디에 놓을 것인지 결정한 다음 위치를 [x,y]로 반환한다.
  28. # showPoints(playerTile,computerTile) : 현재 점수를 출력한다.
  29.  
  30. import random
  31. import sys
  32.  
  33. def drawBoard(board):
  34.     # This function prints out the board that it was passed. Returns None.
  35.     HLINE = '  +---+---+---+---+---+---+---+---+'
  36.     VLINE = '  |   |   |   |   |   |   |   |   |'
  37.  
  38.     print('    1   2   3   4   5   6   7   8')
  39.     print(HLINE)
  40.     for y in range(8):
  41.         print(VLINE)
  42.         print(y+1, end=' ') # end=''키워드 인자를 씀으로 줄 바꿈이 일어나지 않고 빈 문자를 찍도록 한다.
  43.         # print()함수에 자동 추가 문자를 붙이지 않을면 sys.stdout.write()함수를 써야한다.
  44.         for x in range(8):
  45.             print('| %s' % (board[x][y]), end=' ')
  46.         print('|')
  47.         print(VLINE)
  48.         print(HLINE)
  49.  
  50.  
  51. def resetBoard(board):
  52.     # Blanks out the board it is passed, except for the original starting position.
  53.     for x in range(8):
  54.         for y in range(8):
  55.             board[x][y] = ' '
  56.  
  57.     # Starting pieces:
  58.     board[3][3] = 'X'
  59.     board[3][4] = 'O'
  60.     board[4][3] = 'O'
  61.     board[4][4] = 'X'
  62.  
  63.  
  64. def getNewBoard():
  65.     # Creates a brand new, blank board data structure.
  66.     board = [] # board는 8개의 리스트로 된 리스트이며, 리스트는 8개의 문자열을 가지고 있다. 8*8
  67.     for i in range(8):
  68.         board.append([' '] * 8)
  69.  
  70.     return board
  71.  
  72.  
  73. def isValidMove(board, tile, xstart, ystart):
  74.     # Returns False if the player's move on space xstart, ystart is invalid.
  75.     # If it is a valid move, returns a list of spaces that would become the player's if they made a move here.
  76.     if board[xstart][ystart] != ' ' or not isOnBoard(xstart, ystart):
  77.         return False
  78.  
  79.     board[xstart][ystart] = tile # temporarily set the tile on the board.
  80.  
  81.     if tile == 'X':
  82.         otherTile = 'O'
  83.     else:
  84.         otherTile = 'X'
  85.  
  86.     tilesToFlip = [] # 움직임을 통해 뒤집힐 상대편 타일의 리스트를 반환한다.
  87.     # 0. 순서대로 8방향을 조사한다.
  88.     for xdirection, ydirection in [[01][11][10][1, -1][0, -1][-1, -1][-10][-11]]:
  89.         x, y = xstart, ystart
  90.  
  91.         # 1. 조사를 위해 각 방향으로 한칸 씩 움직여본다.
  92.         x += xdirection # first step in the direction
  93.         y += ydirection # first step in the direction
  94.         # 2-1. x,y가 유효하며, 이동한 위치에 상대방 타일이 있을 경우 -> 상대방 타일을 뒤집을 수 있는지 조사한다.
  95.         if isOnBoard(x, y) and board[x][y] == otherTile:
  96.             # There is a piece belonging to the other player next to our piece.
  97.             # 3. 조사를 위해 각 방향으로 한칸 씩 움직여본다.
  98.             x += xdirection
  99.             y += ydirection
  100.             # 3.1 x,y가 유효하지 않으면 #0으로 이동한다.
  101.             if not isOnBoard(x, y):
  102.                 continue
  103.             # 3.2 상대방의 타일일 때까지 조사한다.
  104.             while board[x][y] == otherTile:
  105.                 x += xdirection
  106.                 y += ydirection
  107.                 # 4. x,y가 유효하지 않으면 while문을 종료한다.
  108.                 if not isOnBoard(x, y)# break out of while loop, then continue in for loop
  109.                     break
  110.             # 3.3 x,y가 유효하지 않으면 #0으로 이동한다.
  111.             if not isOnBoard(x, y):
  112.                 continue
  113.             # 3.4 조사를 위해 상대방의 타일 이후의 자신의 타일이 발견된다면,
  114.             if board[x][y] == tile:
  115.                 # There are pieces to flip over. Go in the reverse direction until we reach the original space, noting all the tiles along the way.
  116.                 while True:
  117.                     x -= xdirection
  118.                     y -= ydirection
  119.                     # 5. x,y가 다시 시작 지점으로 이동했다면 while문 break
  120.                     if x == xstart and y == ystart:
  121.                         break
  122.                     tilesToFlip.append([x, y]) # 뒤집을 수 있는 타일을 계속 저장한다.
  123.  
  124.     board[xstart][ystart] = ' ' # restore the empty space
  125.     if len(tilesToFlip) == 0# If no tiles were flipped, this is not a valid move.
  126.         return False
  127.     return tilesToFlip
  128.  
  129.  
  130. def isOnBoard(x, y):
  131.     # Returns True if the coordinates are located on the board.
  132.     return x >= 0 and x <= 7 and y >= 0 and y <=7 # x,y좌표가 0~7 범위 안에 들어야 보드판 위에 있는 것이다.
  133.  
  134.  
  135. def getBoardWithValidMoves(board, tile):
  136.     # Returns a new board with . marking the valid moves the given player can make.
  137.     # 플레이어에게 힌트를 주기위해 가능한 움직임을 board로 리턴한다.
  138.     dupeBoard = getBoardCopy(board)
  139.  
  140.     for x, y in getValidMoves(dupeBoard, tile):
  141.         dupeBoard[x][y] = '.'
  142.     return dupeBoard
  143.  
  144.  
  145. def getValidMoves(board, tile):
  146.     # Returns a list of [x,y] lists of valid moves for the given player on the given board.
  147.     validMoves = []
  148.  
  149.     for x in range(8):
  150.         for y in range(8):
  151.             if isValidMove(board, tile, x, y) != False: # 가능한 움직인인지 isValidMove로 판별하고
  152.                 validMoves.append([x, y])  
  153.     return validMoves # 가능한 위치를 8*8리스트(즉 보드)로 리턴한다.
  154.  
  155.  
  156. def getScoreOfBoard(board):
  157.     # Determine the score by counting the tiles. Returns a dictionary with keys 'X' and 'O'.
  158.     # score의 점수 매기는 것은 보드에서 각 플레이어당 타일의 개수이다.
  159.     xscore = 0
  160.     oscore = 0
  161.     for x in range(8):
  162.         for y in range(8):
  163.             if board[x][y] == 'X':
  164.                 xscore += 1
  165.             if board[x][y] == 'O':
  166.                 oscore += 1
  167.     return {'X':xscore, 'O':oscore} # 딕셔너리로 반환한다.
  168.  
  169.  
  170. def enterPlayerTile():
  171.     # Lets the player type which tile they want to be.
  172.     # Returns a list with the player's tile as the first item, and the computer's tile as the second.
  173.     tile = ''
  174.     while not (tile == 'X' or tile == 'O'):
  175.         print('Do you want to be X or O?')
  176.         tile = input().upper() #플레이어의 타일을 고르도록한다.
  177.  
  178.     # the first element in the tuple is the player's tile, the second is the computer's tile.
  179.     # 리스트로 반환한다.
  180.     if tile == 'X':
  181.         return ['X''O']
  182.     else:
  183.         return ['O''X']
  184.  
  185.  
  186. def whoGoesFirst():
  187.     # Randomly choose the player who goes first.
  188.     if random.randint(01) == 0# 랜덤수를 통해 누가 먼저할 지 결정한다.
  189.         return 'computer'
  190.     else:
  191.         return 'player'
  192.  
  193.  
  194. def playAgain():
  195.     # This function returns True if the player wants to play again, otherwise it returns False.
  196.     print('Do you want to play again? (yes or no)')
  197.     return input().lower().startswith('y') # input()로 받아들여진 문자열을 소문자로 바꾸고, 'y'로 시작하는지 판별한다.
  198.  
  199.  
  200. def makeMove(board, tile, xstart, ystart):
  201.     # Place the tile on the board at xstart, ystart, and flip any of the opponent's pieces.
  202.     # Returns False if this is an invalid move, True if it is valid.
  203.     tilesToFlip = isValidMove(board, tile, xstart, ystart) # 뒤집일 수 있는 상대편의 타일의 리스트
  204.  
  205.     if tilesToFlip == False:
  206.         return False
  207.  
  208.     board[xstart][ystart] = tile
  209.     for x, y in tilesToFlip:
  210.         board[x][y] = tile # 플레이어의 타일로 변경한다.
  211.     return True
  212.  
  213.  
  214. def getBoardCopy(board):
  215.     # Make a duplicate of the board list and return the duplicate.
  216.     dupeBoard = getNewBoard() # 보드를 복사해온다.
  217.  
  218.     for x in range(8):
  219.         for y in range(8):
  220.             dupeBoard[x][y] = board[x][y] # 복사본에 현 보드를 저장한다.
  221.  
  222.     return dupeBoard
  223.  
  224.  
  225. def isOnCorner(x, y):
  226.     # Returns True if the position is in one of the four corners.
  227.     # 0~7의 인덱스중 사각형의 모서리 위치를 리턴한다.
  228.     return (x == 0 and y == 0) or (x == 7 and y == 0) or (x == 0 and y == 7) or (x == 7 and y == 7)
  229.  
  230.  
  231. def getPlayerMove(board, playerTile):
  232.     # Let the player type in their move.
  233.     # Returns the move as [x, y] (or returns the strings 'hints' or 'quit')
  234.     DIGITS1TO8 = '1 2 3 4 5 6 7 8'.split()
  235.     while True:
  236.         print('Enter your move, or type quit to end the game, or hints to turn off/on hints.')
  237.         move = input().lower()
  238.         if move == 'quit'# 종료
  239.             return 'quit'
  240.         if move == 'hints'# 힌트
  241.             return 'hints'
  242.  
  243.         if len(move) == 2 and move[0] in DIGITS1TO8 and move[1] in DIGITS1TO8: # 입력이 두 글자,1~8의 숫자일 경우
  244.             x = int(move[0]) - 1 # 인덱스는 0부터 시작하므로 1을 뺀다.
  245.             y = int(move[1]) - 1
  246.             if isValidMove(board, playerTile, x, y) == False: # 유효한 움직임이 아닐 경우 while문 계속 돌린다
  247.                 continue
  248.             else:
  249.                 break
  250.         else# 조건이 아니면 유효한 숫자가 아니다.
  251.             print('That is not a valid move. Type the x digit (1-8), then the y digit (1-8).')
  252.             print('For example, 81 will be the top-right corner.')
  253.  
  254.     return [x, y] # 리스트를 반환한다.
  255.  
  256.  
  257. def getComputerMove(board, computerTile):
  258.     # Given a board and the computer's tile, determine where to
  259.     # move and return that move as a [x, y] list.
  260.  
  261.     ## 인공지능 구현
  262.    
  263.     possibleMoves = getValidMoves(board, computerTile) # 컴퓨터가 유효한 움직임을 리스트로 저장한다.
  264.  
  265.     # randomize the order of the possible moves
  266.     random.shuffle(possibleMoves) # random모듈의 shuffle로 리스트를 섞는다.
  267.  
  268.     # 1. always go for a corner if available. 코너에 놓는 것이 가장 좋다.
  269.     for x, y in possibleMoves:
  270.         if isOnCorner(x, y):
  271.             return [x, y]
  272.  
  273.     # 2. Go through all the possible moves and remember the best scoring move.
  274.     #   가장 높은 점수를 받을 수 있는 위치에 대한 리스트를 생성한다.
  275.     bestScore = -1
  276.     for x, y in possibleMoves: # 가능한 움직임 중에서
  277.         dupeBoard = getBoardCopy(board)
  278.         makeMove(dupeBoard, computerTile, x, y)
  279.         score = getScoreOfBoard(dupeBoard)[computerTile]
  280.         if score > bestScore: # 최고 점수 갱신한다
  281.             bestMove = [x, y]
  282.             bestScore = score
  283.     return bestMove
  284.  
  285.  
  286. def showPoints(playerTile, computerTile):
  287.     # Prints out the current score.
  288.     scores = getScoreOfBoard(mainBoard)
  289.     print('You have %s points. The computer has %s points.' % (scores[playerTile], scores[computerTile]))
  290.  
  291.  
  292.  
  293. print('Welcome to Reversi!')
  294.  
  295. while True:
  296.     # Reset the board and game.
  297.     mainBoard = getNewBoard()
  298.     resetBoard(mainBoard)
  299.     playerTile, computerTile = enterPlayerTile()
  300.     showHints = False
  301.     turn = whoGoesFirst()
  302.     print('The ' + turn + ' will go first.')
  303.  
  304.     while True:
  305.         if turn == 'player':
  306.             # Player's turn.
  307.             if showHints:
  308.                 validMovesBoard = getBoardWithValidMoves(mainBoard, playerTile)
  309.                 drawBoard(validMovesBoard)
  310.             else:
  311.                 drawBoard(mainBoard)
  312.             showPoints(playerTile, computerTile)
  313.             move = getPlayerMove(mainBoard, playerTile)
  314.             if move == 'quit':
  315.                 print('Thanks for playing!')
  316.                 sys.exit() # terminate the program
  317.             elif move == 'hints':
  318.                 showHints = not showHints
  319.                 continue
  320.             else:
  321.                 makeMove(mainBoard, playerTile, move[0], move[1])
  322.  
  323.             if getValidMoves(mainBoard, computerTile) == []:
  324.                 break
  325.             else:
  326.                 turn = 'computer'
  327.  
  328.         else:
  329.             # Computer's turn.
  330.             drawBoard(mainBoard)
  331.             showPoints(playerTile, computerTile)
  332.             input('Press Enter to see the computer\'s move.')
  333.             x, y = getComputerMove(mainBoard, computerTile)
  334.             makeMove(mainBoard, computerTile, x, y)
  335.  
  336.             if getValidMoves(mainBoard, playerTile) == []:
  337.                 break
  338.             else:
  339.                 turn = 'player'
  340.  
  341.     # Display the final score.
  342.     drawBoard(mainBoard)
  343.     scores = getScoreOfBoard(mainBoard)
  344.     print('X scored %s points. O scored %s points.' % (scores['X'], scores['O']))
  345.     if scores[playerTile] > scores[computerTile]:
  346.         print('You beat the computer by %s points! Congratulations!' % (scores[playerTile] - scores[computerTile]))
  347.     elif scores[playerTile] < scores[computerTile]:
  348.         print('You lost. The computer beat you by %s points.' % (scores[computerTile] - scores[playerTile]))
  349.     else:
  350.         print('The game was a tie!')
  351.  
  352.     if not playAgain():
  353.         break
  354.  


'Computer Language' 카테고리의 다른 글

03 Python 조건문  (0) 2015.01.28
02 Python 자료형  (0) 2015.01.28
CaesarCiper.py  (0) 2015.01.16
SonarGame.py  (0) 2015.01.14
CartesianCorrdinateSystems.py  (0) 2015.01.14
Comments