[Deep Learning] Implementing a game-based AI, starting with wuziqi (2)
Well, today we are going to start playing wuziqi and start talking to our friends about AI.
Last night we had implemented a logical part of wuziqi. In fact, there is a rule in which AI can be started, but it is not intuitive enough, we recommend that you first develop the UI of wuziqi. So today we are engaged in the UI.
The logic part is here: [deep learning] Implementing a game-type AI, starting from wuziqi (1)
Partner: What? Once again, I want to save 10 thousand words, say well where deep learning is, say well where reinforcement learning is, and today it's wuziqi ......
Me: It's wuziqi. AI cannot be lacking in scenarios. If there is no scenario to talk about AI, It's just empty talk. You have to have a game first. Besides, even though we had a logic, we should have at least a page to test it. In case the logic of the scenario is not correct, we should have a hammer!
Old Luo: What does it matter to me?
Now, let's get back to the question. At the beginning, the design is the separation of logic and UI. in the previous article, we implemented the logic part. Today we will implement the UI part and give our wuziqi a UI.
(2) Implementation of chess UI
If Python is used as the UI of Wuzi game, we will use PyGame here. Of course, there are other libraries. To be honest, I have never done much in Python UI, I will not start the basic usage and various knowledge of PyGame. After all, this is not the focus. Interested partners can Google themselves. I also learn and use it. Haha!
Since I have to have materials for the UI, I found a chessboard on the Internet:
And two pawns in black and white:
PS: to make the UI look good, the pawns are circular, preferably in PNG format, with Alpha channel and transparent outside. In addition, I don't know if these images will be compressed into other formats after being uploaded. I put a pack at the end of the article.
Create a directory "UI" in our previous project, and put the Board name chessboard.jpg under the directory. The two pieces are named piece_black.png?piece_white.png respectively and under the directory.
Look at the attribute. The checker is 540*540 pixels, And the chess piece is 32*32 pixels. Write down the number, and the checker we are looking for has an edge, the edges are about 22 pixels away from the first line. To use render, use these numbers.
There are 14 grids in the middle of the 15 lines, so the distance between the lines is the total width minus the two edges and then divided by the number of grids: (540-22*2)/14 seems to be different.
Okay. Create a file render. py, let's put the numbers that we just put in. By the way, the import is also imported, such as pygame, such as yesterday's definition and yesterday's five pieces logic:
#coding:utf-8import pygamefrom pygame.locals import *from consts import *from gobang import GoBang#IMAGE_PATH = '/Users/phantom/Projects/AI/gobang/UI/'IMAGE_PATH = 'UI/'WIDTH = 540HEIGHT = 540MARGIN = 22GRID = (WIDTH - 2 * MARGIN) / (N - 1)PIECE = 32
Then we define a new class, GameRender, and render. during initialization, we bind a logic class, initialize pygame, set the form size, and load the loaded resources first, the code is relatively simple and there is no reason why pygame is used in this way. pygame's friends are interested in Google.
Render we still consider defining a current to represent the current step, black first, so current is defined as black.
Class GameRender (object): def _ init _ (self, gobang): # bind the logic class self. _ gobang = gobang # Start of the black game self. _ currentPieceState = ChessboardState. BLACK # initialize pygame. init () # pygame. display. set_mode (width, height), flags, depth) self. _ screen = pygame. display. set_mode (WIDTH, HEIGHT), 0, 32) pygame. display. set_caption ('five chess ai') # UI resource self. _ ui_chessboard = pygame. image. load (IMAGE_PATH + 'chessboard.jpg '). convert () self. _ ui_piece_black = pygame. image. load (IMAGE_PATH + 'piece_black.png '). convert_alpha () self. _ ui_piece_white = pygame. image. load (IMAGE_PATH + 'piece_white.png '). convert_alpha ()
Render class, all kinds of draw, right, indeed. However, there is a problem here.
In the previous logic class, we defined a two-dimensional array chessMap. Remember? Let's look at the definition of the logic class GoBang:
class GoBang(object): def __init__(self): self.__chessMap = [[ChessboardState.EMPTY for j in range(N)] for i in range(N)] self.__currentI = -1 self.__currentJ = -1 self.__currentState = ChessboardState.EMPTY
Let's first think about a question: how does the coordinate in chessMap correspond to the coordinate of our board? In chessMap I, j is from 0 to 14, 0 to 14; on our board, when we render, that's pixel-based. The chessboard is 0 to 540 pixels. Strictly speaking, it is 540 minus two edges, and 22 to 518 pixels. Please first check the relationship. Well, perform a coordinate transformation to convert the subscript I and j of the chess piece into pixels x and y. Calculate from the edge, add a GRID of the size of each adjacent piece, then if our piece is to be placed, it should be placed in the middle of the piece, so x, y minus the size of half a pawn, and the code will be two lines, which is clearer:
Def coordinate_transform_map2pixel (self, I, j): # convert the logical coordinates in chessMap to the draw coordinates on the UI. return MARGIN + j * GRID-PIECE/2, MARGIN + I * GRID-PIECE/2
Well, now we can read the status from the logic class and draw it. Let's take another look at the coordinate transformation. We don't need to change it in turn. Yes, it is really necessary to get x and y on the UI when playing a game, right? We have to set the status in the logic class, so at this time, we need to convert the x and y coordinates into the I and j coordinates. The similar logic is not detailed.
Here, if we are a lazy hacker, isn't the previous ing function in the formula:
X = MARGIN + j * GRID-PIECE/2
Y = MARGIN + I * GRID-PIECE/2
Make a shift, deduce the two sides of the equation, express j with x, and I use y to express it. Then we can:
I = (y-MARGIN + PIECE/2)/GRID
J = (x-MARGIN + PIECE/2)/GRID
The careful friends found that I and j may not be integers. The first coordinate obtained is of course the mouse. This would have been a deviation, and it would not have been so good, and the GRID does not seem to be an integer. I don't know what it is after a while. OK, let's Round.
Another friend said, isn't there an edge on the chessboard? The MARGIN always reminds us that there is an edge. if I click on the edge, will there be negative values or values greater than N. Yes, you have a good idea. You have to judge the boundary. This should be the case. You can write the code:
Def coordinate_transform_pixel2map (self, x, y): # transformation from the draw coordinate on the UI to the logical coordinate in chessMap I, j = int (round (y-MARGIN + PIECE/2)/GRID), int (round (x-MARGIN + PIECE/2)/GRID )) # if MAGIN exists and edge locations are excluded, causing I and j to cross-border. if I <0 or I> = N or j <0 or j> = N: return None, None else: return I, j
Now, the coordinate ing has been completed. We can finally draw, draw, and draw. Well, let's draw, draw a board, and then draw a chess piece, when the color of a piece is set to the color, skip the blank part:
Def draw_chess (self): # checker self. _ screen. BITs (self. _ ui_chessboard, (0, 0) # chess piece for I in range (0, N): for j in range (0, N): x, y = self. coordinate_transform_map2pixel (I, j) state = self. _ gobang. get_chessboard_state (I, j) if state = ChessboardState. BLACK: self. _ screen. BITs (self. _ ui_piece_black, (x, y) elif state = ChessboardState. WHITE: self. _ screen. BITs (self. _ ui_piece_white, (x, y) else: # ChessboardState. EMPTY pass
To better experience playing chess, do we have to draw a piece over the mouse? It seems better to just click it:
Def draw_mouse (self): # coordinates of the mouse x, y = pygame. mouse. get_pos () # Move the pawn following the mouse if self. _ currentPieceState = ChessboardState. BLACK: self. _ screen. BITs (self. _ ui_piece_black, (x-PIECE/2, y-PIECE/2) else: self. _ screen. BITs (self. _ ui_piece_white, (x-PIECE/2, y-PIECE/2 ))
If there are five consecutive same-color pawns and the result of winning is to be displayed, a draw is returned:
Def draw_result (self, result): font = pygame. font. font ('/Library/Fonts/Songti. ttc ', 50) tips = u "this council ends:" if result = ChessboardState. BLACK: tips = tips + u "" elif result = ChessboardState. WHITE: tips = tips + u "" else: tips = tips + u "Draw" text = font. render (tips, True, (255, 0, 0) self. _ screen. BITs (text, (WIDTH/2-200, HEIGHT/2-50 ))
What's worse?
Right, the logic of playing chess hasn't been done yet. Click the mouse and place a chess piece on the board. We just read the chessMap in the logic class when we were playing chess, go to the set status:
Def one_step (self): I, j = None, None # click mouse_button = pygame. mouse. get_pressed () # Left click if mouse_button [0]: x, y = pygame. mouse. get_pos () I, j = self. coordinate_transform_pixel2map (x, y) if not I is None and not j is None: # if self is already in the grid. _ gobang. get_chessboard_state (I, j )! = ChessboardState. EMPTY: return False else: self. _ gobang. set_chessboard_state (I, j, self. _ currentPieceState) return True return False
Now we don't have AI. We don't have to do anything about it. First, let's make a everyone-to-Everyone comparison. Then we will add a color switching function:
def change_state(self): if self.__currentPieceState == ChessboardState.BLACK: self.__currentPieceState = ChessboardState.WHITE else: self.__currentPieceState = ChessboardState.BLACK
Okay, what's worse? If it seems like a render, it feels similar. Let's use the main function to slide the code and create a new game. py, here we leave at least one framework for AI in the main function:
Import pygamefrom pygame. locals import * from sys import exitfrom consts import * from gobang import GoBangfrom render import GameRender # from gobang_ai import GobangAIif _ name _ = '_ main __': gobang = GoBang () render = GameRender (gobang) # leave an interface for AI First # ai = GobangAI (gobang, ChessboardState. WHITE) result = ChessboardState. EMPTY enable_ai = False while True: # capture the pygame event in pygame. event. get (): # Exit the program if event. type = QUIT: exit () elif event. type = MOUSEBUTTONDOWN: # if render is successfully played. one_step (): result = gobang. get_chess_result () else: continue if result! = ChessboardState. EMPTY: break if enable_ai: # ai. one_step () result = gobang. get_chess_result () else: render. change_state () # Draw render. draw_chess () render. draw_mouse () if result! = ChessboardState. EMPTY: render. draw_result (result) # refresh pygame. display. update ()
Okay. Run it and try again. If there is no AI, we can pull two friends to fight against the problem. It is really not possible to have a hand or a right hand. It seems okay, and the logic is okay:
Sort out the full version of render. py:
# Coding: utf-8import pygamefrom pygame. locals import * from consts import * from gobang import GoBang # IMAGE_PATH = '/Users/phantom/Projects/AI/gobang/UI/'image _ PATH = 'ui/'width = 540 HEIGHT = 540 MARGIN = 22 GRID = (WIDTH-2 * MARGIN) /(N-1) PIECE = 32 class GameRender (object): def _ init _ (self, gobang): # bind the logic class self. _ gobang = gobang # Start of the black game self. _ currentPieceState = ChessboardState. BLACK # initialize pyga Me pygame. init () # pygame. display. set_mode (width, height), flags, depth) self. _ screen = pygame. display. set_mode (WIDTH, HEIGHT), 0, 32) pygame. display. set_caption ('five chess ai') # UI resource self. _ ui_chessboard = pygame. image. load (IMAGE_PATH + 'chessboard.jpg '). convert () self. _ ui_piece_black = pygame. image. load (IMAGE_PATH + 'piece_black.png '). convert_alpha () self. _ ui_piece_white = pygame. image. load (IMAG E_PATH + 'piece_white.png '). convert_alpha () def coordinate_transform_map2pixel (self, I, j): # convert the logical coordinates in chessMap to the draw coordinates on the UI. return MARGIN + j * GRID-PIECE/2, MARGIN + I * GRID-PIECE/2 def coordinate_transform_pixel2map (self, x, y): # transformation from the draw coordinate on the UI to the logical coordinate in chessMap I, j = int (round (y-MARGIN + PIECE/2)/GRID), int (round (x-MARGIN + PIECE/2)/GRID )) # If MAGIN exists, edge locations are excluded, causing I and j to cross-border I F I <0 or I> = N or j <0 or j> = N: return None, None else: return I, j def draw_chess (self): # board self. _ screen. BITs (self. _ ui_chessboard, (0, 0) # chess piece for I in range (0, N): for j in range (0, N): x, y = self. coordinate_transform_map2pixel (I, j) state = self. _ gobang. get_chessboard_state (I, j) if state = ChessboardState. BLACK: self. _ screen. BITs (self. _ ui_piece_black, (x, y) elif state = Chessboar DState. WHITE: self. _ screen. BITs (self. _ ui_piece_white, (x, y) else: # ChessboardState. EMPTY pass def draw_mouse (self): # coordinates of the mouse x, y = pygame. mouse. get_pos () # Move the pawn following the mouse if self. _ currentPieceState = ChessboardState. BLACK: self. _ screen. BITs (self. _ ui_piece_black, (x-PIECE/2, y-PIECE/2) else: self. _ screen. BITs (self. _ ui_piece_white, (x-PIECE/2, y-PIECE/2) def draw_result (self, r Esult): font = pygame. font. font ('/Library/Fonts/Songti. ttc ', 50) tips = u "this council ends:" if result = ChessboardState. BLACK: tips = tips + u "" elif result = ChessboardState. WHITE: tips = tips + u "" else: tips = tips + u "Draw" text = font. render (tips, True, (255, 0, 0) self. _ screen. BITs (text, (WIDTH/2-200, HEIGHT/2-50) def one_step (self): I, j = None, None # click mouse_button = pygame.mo Use. get_pressed () # Left click if mouse_button [0]: x, y = pygame. mouse. get_pos () I, j = self. coordinate_transform_pixel2map (x, y) if not I is None and not j is None: # if self is already in the grid. _ gobang. get_chessboard_state (I, j )! = ChessboardState. EMPTY: return False else: self. _ gobang. set_chessboard_state (I, j, self. _ currentPieceState) return True return False def change_state (self): if self. _ currentPieceState = ChessboardState. BLACK: self. _ currentPieceState = ChessboardState. WHITE else: self. _ currentPieceState = ChessboardState. BLACK
Okay, that's it ~
I made a package for the UI material and put it here:
Click here to download the UI Material
............ Postscript ............
Me: Let's stop talking about AI tomorrow, because we have five games ~
Friend: Okay, finally.
Me: Wait, tomorrow? NO, I'm confused. The next article will definitely start to engage in AI. Maybe I will not be able to write a blog tomorrow --
Little friend: save another 10 thousand words!
Me: at least two articles are written every week. OK?
Friend: What else can I do ......