preface
This time, let's write a simple game that supports online combat and a Gobang game that supports LAN online combat. No more nonsense. Let's start happily~
Effect demonstration
development tool
Python version: 3.6 four
Related modules:
pygame module;
PyQt5 module;
And some Python built-in modules.
Environment construction
Install Python and add it to the environment variable. pip can install the relevant modules required.
Principle introduction
Here is a brief introduction to the principle. The code is mainly written in PyQt5. pygame is only used to play some sound effects. First, design and implement a game main interface:
The code implementation is as follows:
'''Game start interface''' class gameStartUI(QWidget): def __init__(self, parent=None, **kwargs): super(gameStartUI, self).__init__(parent) self.setFixedSize(760, 650) self.setWindowTitle('Gobang-🛰️: ilove-python') self.setWindowIcon(QIcon(cfg.ICON_FILEPATH)) # Background picture palette = QPalette() palette.setBrush(self.backgroundRole(), QBrush(QPixmap(cfg.BACKGROUND_IMAGEPATHS.get('bg_start')))) self.setPalette(palette) # Button # --Man machine combat self.ai_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('ai'), self) self.ai_button.move(250, 200) self.ai_button.show() self.ai_button.click_signal.connect(self.playWithAI) # --Online battle self.online_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('online'), self) self.online_button.move(250, 350) self.online_button.show() self.online_button.click_signal.connect(self.playOnline) '''Man machine combat''' def playWithAI(self): self.close() self.gaming_ui = playWithAIUI(cfg) self.gaming_ui.exit_signal.connect(lambda: sys.exit()) self.gaming_ui.back_signal.connect(self.show) self.gaming_ui.show() '''Online battle''' def playOnline(self): self.close() self.gaming_ui = playOnlineUI(cfg, self) self.gaming_ui.show()
Yes pyqt5, you can write such an interface. It's nothing special. Remember to bind the signals triggered by the man-machine battle and online battle buttons to the functions of man-machine battle and online battle respectively.
The effect is like this:
The main code implementation is as follows:
'''Man machine combat''' class playWithAIUI(QWidget): back_signal = pyqtSignal() exit_signal = pyqtSignal() send_back_signal = False def __init__(self, cfg, parent=None, **kwargs): super(playWithAIUI, self).__init__(parent) self.cfg = cfg self.setFixedSize(760, 650) self.setWindowTitle('Gobang-🛰️: ilove-python') self.setWindowIcon(QIcon(cfg.ICON_FILEPATH)) # Background picture palette = QPalette() palette.setBrush(self.backgroundRole(), QBrush(QPixmap(cfg.BACKGROUND_IMAGEPATHS.get('bg_game')))) self.setPalette(palette) # Button self.home_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('home'), self) self.home_button.click_signal.connect(self.goHome) self.home_button.move(680, 10) self.startgame_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('startgame'), self) self.startgame_button.click_signal.connect(self.startgame) self.startgame_button.move(640, 240) self.regret_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('regret'), self) self.regret_button.click_signal.connect(self.regret) self.regret_button.move(640, 310) self.givein_button = PushButton(cfg.BUTTON_IMAGEPATHS.get('givein'), self) self.givein_button.click_signal.connect(self.givein) self.givein_button.move(640, 380) # Drop sign self.chessman_sign = QLabel(self) sign = QPixmap(cfg.CHESSMAN_IMAGEPATHS.get('sign')) self.chessman_sign.setPixmap(sign) self.chessman_sign.setFixedSize(sign.size()) self.chessman_sign.show() self.chessman_sign.hide() # Chessboard (19 * 19 matrix) self.chessboard = [[None for i in range(19)] for _ in range(19)] # History (for repentance) self.history_record = [] # Is it in the game self.is_gaming = True # Victorious side self.winner = None self.winner_info_label = None # Whose turn is it to assign colors self.player_color = 'white' self.ai_color = 'black' self.whoseround = self.player_color # Instantiate ai self.ai_player = aiGobang(self.ai_color, self.player_color) # Falling sound loading pygame.mixer.init() self.drop_sound = pygame.mixer.Sound(cfg.SOUNDS_PATHS.get('drop')) '''Left mouse click event-Player turn''' def mousePressEvent(self, event): if (event.buttons() != QtCore.Qt.LeftButton) or (self.winner is not None) or (self.whoseround != self.player_color) or (not self.is_gaming): return # Ensure that the response is only within the chessboard range if event.x() >= 50 and event.x() <= 50 + 30 * 18 + 14 and event.y() >= 50 and event.y() <= 50 + 30 * 18 + 14: pos = Pixel2Chesspos(event) # There was no one in the place where they were guaranteed to fall if self.chessboard[pos[0]][pos[1]]: return # Instantiate a chess piece and display it c = Chessman(self.cfg.CHESSMAN_IMAGEPATHS.get(self.whoseround), self) c.move(event.pos()) c.show() self.chessboard[pos[0]][pos[1]] = c # The falling sound sounded self.drop_sound.play() # Finally, the falling position mark follows the falling position self.chessman_sign.show() self.chessman_sign.move(c.pos()) self.chessman_sign.raise_() # Record this fall self.history_record.append([*pos, self.whoseround]) # Did you win self.winner = checkWin(self.chessboard) if self.winner: self.showGameEndInfo() return # Switch round side (actually change color) self.nextRound() '''Left mouse button release operation-Call computer round''' def mouseReleaseEvent(self, event): if (self.winner is not None) or (self.whoseround != self.ai_color) or (not self.is_gaming): return self.aiAct() '''Computer automatically-AI round''' def aiAct(self): if (self.winner is not None) or (self.whoseround == self.player_color) or (not self.is_gaming): return next_pos = self.ai_player.act(self.history_record) # Instantiate a chess piece and display it c = Chessman(self.cfg.CHESSMAN_IMAGEPATHS.get(self.whoseround), self) c.move(QPoint(*Chesspos2Pixel(next_pos))) c.show() self.chessboard[next_pos[0]][next_pos[1]] = c # The falling sound sounded self.drop_sound.play() # Finally, the falling position mark follows the falling position self.chessman_sign.show() self.chessman_sign.move(c.pos()) self.chessman_sign.raise_() # Record this fall self.history_record.append([*next_pos, self.whoseround]) # Did you win self.winner = checkWin(self.chessboard) if self.winner: self.showGameEndInfo() return # Switch round side (actually change color) self.nextRound() '''Change Luozi square''' def nextRound(self): self.whoseround = self.player_color if self.whoseround == self.ai_color else self.ai_color '''Show game end results''' def showGameEndInfo(self): self.is_gaming = False info_img = QPixmap(self.cfg.WIN_IMAGEPATHS.get(self.winner)) self.winner_info_label = QLabel(self) self.winner_info_label.setPixmap(info_img) self.winner_info_label.resize(info_img.size()) self.winner_info_label.move(50, 50) self.winner_info_label.show() '''Admit defeat''' def givein(self): if self.is_gaming and (self.winner is None) and (self.whoseround == self.player_color): self.winner = self.ai_color self.showGameEndInfo() '''Repentance chess-Only our turn can repent''' def regret(self): if (self.winner is not None) or (len(self.history_record) == 0) or (not self.is_gaming) and (self.whoseround != self.player_color): return for _ in range(2): pre_round = self.history_record.pop(-1) self.chessboard[pre_round[0]][pre_round[1]].close() self.chessboard[pre_round[0]][pre_round[1]] = None self.chessman_sign.hide() '''Start the game-The previous game must have ended''' def startgame(self): if self.is_gaming: return self.is_gaming = True self.whoseround = self.player_color for i, j in product(range(19), range(19)): if self.chessboard[i][j]: self.chessboard[i][j].close() self.chessboard[i][j] = None self.winner = None self.winner_info_label.close() self.winner_info_label = None self.history_record.clear() self.chessman_sign.hide() '''Close window event''' def closeEvent(self, event): if not self.send_back_signal: self.exit_signal.emit() '''Return to the game main page''' def goHome(self): self.send_back_signal = True self.close() self.back_signal.emit()
The whole logic is as follows:
After designing and implementing the basic interface of the game, the first default is always the player's first hand (white son) and the computer's second hand (black son). Then, when the supervisor hears that the player clicks the left mouse button to the range where the chessboard grid is located, capture the position. If no one has dropped the position before, the player will successfully drop the position. Otherwise, wait for the player's left mouse button click event again. After the player successfully lands, judge whether the game ends because of the player's landing (that is, there are 5 dice connected with the same color on the chessboard). If the game ends, the game end interface will be displayed, otherwise it's AI's turn to land. The logic of AI falling is similar to that of player falling, and then it's the player's turn to fall, and so on.
It should be noted that in order to ensure the real-time response, the AI drop algorithm should be written into the response to the release event after the left mouse click (interested partners can try to write into the response to the mouse click event, which will cause the player's last drop and the AI's current drop results to be displayed only after the AI calculation is completed and dropped).
The start button is to reset the game. There's nothing to say. Here, in order to avoid some people like to cheat, I wrote the code that I must complete the current game to reset the game.
Because it is against AI, the repentance button directly repents two steps. pop the last two drops from the history list, and then remove the two drops from the corresponding position of the chessboard. It is OK to ensure that only our turn can repent to avoid unexpected logic errors.
There's nothing to say about the admit defeat button, that is, admit defeat and end the game ahead of time.
Next, let's realize the online battle. Here, we choose to use TCP/IP protocol for online communication to realize the online battle. Start the side of the game as the server side first:
Listen by opening a new thread: threading.Thread(target=self.startListen).start() '''Start listening for client connections''' def startListen(self): while True: self.setWindowTitle('Gobang-🛰️: ilove-python -> The server is started successfully, Waiting for client connection') self.tcp_socket, self.client_ipport = self.tcp_server.accept() self.setWindowTitle('Gobang-🛰️: ilove-python -> Client connected, Click the start button to play the game')
The post initiator connects to the server as the client and sends the basic information of the client player:
self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.tcp_socket.connect(self.server_ipport) data = {'type': 'nickname', 'data': self.nickname} self.tcp_socket.sendall(packSocketData(data)) self.setWindowTitle('Gobang-🛰️: ilove-python -> The server has been successfully connected, Click the start button to play the game')
When the client connects to the server, the server also sends the basic player information of the server to the client:
data = {'type': 'nickname', 'data': self.nickname} self.tcp_socket.sendall(packSocketData(data))
Then, both the client and server use the newly opened thread to realize network data listening and receiving:
'''Receive client data''' def receiveClientData(self): while True: data = receiveAndReadSocketData(self.tcp_socket) self.receive_signal.emit(data) '''Receiving server-side data''' def receiveServerData(self): while True: data = receiveAndReadSocketData(self.tcp_socket) self.receive_signal.emit(data)
And make corresponding responses in the main process according to different data received:
'''Response to received data''' def responseForReceiveData(self, data): if data['type'] == 'action' and data['detail'] == 'exit': QMessageBox.information(self, 'Tips', 'Your opponent has quit the game, The game will automatically return to the main interface') self.goHome() elif data['type'] == 'action' and data['detail'] == 'startgame': self.opponent_player_color, self.player_color = data['data'] self.whoseround = 'white' self.whoseround2nickname_dict = {self.player_color: self.nickname, self.opponent_player_color: self.opponent_nickname} res = QMessageBox.information(self, 'Tips', 'Other party's request(again)Start the game, You are%s, Do you agree?' % {'white': 'Baizi', 'black': 'sunspot'}.get(self.player_color), QMessageBox.Yes | QMessageBox.No) if res == QMessageBox.Yes: data = {'type': 'reply', 'detail': 'startgame', 'data': True} self.tcp_socket.sendall(packSocketData(data)) self.is_gaming = True self.setWindowTitle('Gobang-🛰️: ilove-python -> %s Go chess' % self.whoseround2nickname_dict.get(self.whoseround)) for i, j in product(range(19), range(19)): if self.chessboard[i][j]: self.chessboard[i][j].close() self.chessboard[i][j] = None self.history_record.clear() self.winner = None if self.winner_info_label: self.winner_info_label.close() self.winner_info_label = None self.chessman_sign.hide() else: data = {'type': 'reply', 'detail': 'startgame', 'data': False} self.tcp_socket.sendall(packSocketData(data)) elif data['type'] == 'action' and data['detail'] == 'drop': pos = data['data'] # Instantiate a chess piece and display it c = Chessman(self.cfg.CHESSMAN_IMAGEPATHS.get(self.whoseround), self) c.move(QPoint(*Chesspos2Pixel(pos))) c.show() self.chessboard[pos[0]][pos[1]] = c # The falling sound sounded self.drop_sound.play() # Finally, the falling position mark follows the falling position self.chessman_sign.show() self.chessman_sign.move(c.pos()) self.chessman_sign.raise_() # Record this fall self.history_record.append([*pos, self.whoseround]) # Did you win self.winner = checkWin(self.chessboard) if self.winner: self.showGameEndInfo() return # Switch round side (actually change color) self.nextRound() elif data['type'] == 'action' and data['detail'] == 'givein': self.winner = self.player_color self.showGameEndInfo() elif data['type'] == 'action' and data['detail'] == 'urge': self.urge_sound.play() elif data['type'] == 'action' and data['detail'] == 'regret': res = QMessageBox.information(self, 'Tips', 'The other party asks for repentance, Do you agree?', QMessageBox.Yes | QMessageBox.No) if res == QMessageBox.Yes: pre_round = self.history_record.pop(-1) self.chessboard[pre_round[0]][pre_round[1]].close() self.chessboard[pre_round[0]][pre_round[1]] = None self.chessman_sign.hide() self.nextRound() data = {'type': 'reply', 'detail': 'regret', 'data': True} self.tcp_socket.sendall(packSocketData(data)) else: data = {'type': 'reply', 'detail': 'regret', 'data': False} self.tcp_socket.sendall(packSocketData(data)) elif data['type'] == 'reply' and data['detail'] == 'startgame': if data['data']: self.is_gaming = True self.setWindowTitle('Gobang-🛰️: ilove-python -> %s Go chess' % self.whoseround2nickname_dict.get(self.whoseround)) for i, j in product(range(19), range(19)): if self.chessboard[i][j]: self.chessboard[i][j].close() self.chessboard[i][j] = None self.history_record.clear() self.winner = None if self.winner_info_label: self.winner_info_label.close() self.winner_info_label = None self.chessman_sign.hide() QMessageBox.information(self, 'Tips', 'The other party agrees to start the game request, You are%s, The speaker goes first.' % {'white': 'Baizi', 'black': 'sunspot'}.get(self.player_color)) else: QMessageBox.information(self, 'Tips', 'The other party rejected your request to start the game.') elif data['type'] == 'reply' and data['detail'] == 'regret': if data['data']: pre_round = self.history_record.pop(-1) self.chessboard[pre_round[0]][pre_round[1]].close() self.chessboard[pre_round[0]][pre_round[1]] = None self.nextRound() QMessageBox.information(self, 'Tips', 'The other party agreed to your request.') else: QMessageBox.information(self, 'Tips', 'Your request for repentance has been rejected.') elif data['type'] == 'nickname': self.opponent_nickname = data['data']
Place of modification
You must click the start button and get the other party's consent before you can officially start the game. The repentance button can only be pressed in the other party's turn. After the other party agrees to repent, you need to remember to switch the falling party back to yourself. Then a push button is added, which can only be pressed after the opponent's turn. The above is all the code changes.
This is the end of the article. Thank you for watching, Python 24 games series , the next article shares 2048 games
In order to thank readers, I would like to share some of my recent collection of programming dry goods with you and give back to every reader. I hope I can help you.
All done ~ get the complete source code by private letter..
Previous review
Python implementation of bomber games
Python implementation of classic bean eating games
Python real dinosaur jump games
Python implementation of simple version of aircraft war games
Tetris games implemented in Python
Python implements "Bunny and Bun" game
Python implementation of eight tone symbol games
Python implements the classic 90 tank war
Python implementation of fruit and gold coin games
Python implementation of table tennis games
Python implementation of brick games