preface
This time we will write a simple support online games, support LAN online gobang games. Without further ado, let’s begin happily ~
Results demonstrate
The development tools
Python version: 3.6.4
Related modules:
Pygame module;
PyQt5 module;
And some modules that come with Python.
Environment set up
Install Python and add it to the environment variables. PIP installs the required modules.
Introduction of the principle
The code is mostly written in PyQt5, and PyGame is only used to play some sound effects. First, design and implement the main interface of the game:
Code implementation is as follows:
"Game Start screen"
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 Image
palette = QPalette()
palette.setBrush(self.backgroundRole(), QBrush(QPixmap(cfg.BACKGROUND_IMAGEPATHS.get('bg_start'))))
self.setPalette(palette)
# button
# -- Man versus machine
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)
# -- Play online
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 Vs. Man"
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()
Play Online
def playOnline(self) :
self.close()
self.gaming_ui = playOnlineUI(cfg, self)
self.gaming_ui.show()
Copy the code
Pyqt5 should be able to write such an interface, nothing special, remember to the man-machine and play online two buttons triggered after the signal bound to the man-machine and play functions.
The effect looks something like this:
The main code implementation is as follows:
"Man Vs. Man"
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 Image
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 flag
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()
# Checkerboard (19 by 19 matrix)
self.chessboard = [[None for i in range(19)] for _ in range(19)]
# History (for chess)
self.history_record = []
# In the game or not
self.is_gaming = True
# victory party
self.winner = None
self.winner_info_label = None
Whose turn is it to place the ball
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)
# Drop child 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 responses are 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)
# Ensure that the place where the pin is placed has not been placed
if self.chessboard[pos[0]][pos[1]] :return
# Instantiate a chess piece and display
c = Chessman(self.cfg.CHESSMAN_IMAGEPATHS.get(self.whoseround), self)
c.move(event.pos())
c.show()
self.chessboard[pos[0]][pos[1]] = c
# The drop sounds
self.drop_sound.play()
The # final drop position flag follows the drop position
self.chessman_sign.show()
self.chessman_sign.move(c.pos())
self.chessman_sign.raise_()
Record this drop
self.history_record.append([*pos, self.whoseround])
# Victory or not
self.winner = checkWin(self.chessboard)
if self.winner:
self.showGameEndInfo()
return
# Change the color of the turn
self.nextRound()
Left mouse Button release operation - Call computer turn
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 automatic down -AI turn"
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
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 drop sounds
self.drop_sound.play()
The # final drop position flag follows the drop position
self.chessman_sign.show()
self.chessman_sign.move(c.pos())
self.chessman_sign.raise_()
Record this drop
self.history_record.append([*next_pos, self.whoseround])
# Victory or not
self.winner = checkWin(self.chessboard)
if self.winner:
self.showGameEndInfo()
return
# Change the color of the turn
self.nextRound()
Change drop square
def nextRound(self) :
self.whoseround = self.player_color if self.whoseround == self.ai_color else self.ai_color
Display end of game result
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()
"' give up ' ' '
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()
"Contrite - Only on our turn can we contrite."
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 be over.
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 main game page.
def goHome(self) :
self.send_back_signal = True
self.close()
self.back_signal.emit()
Copy the code
The whole logic goes like this:
After designing and implementing the basic interface of the game, the default is always the player first (white) and the computer second (black). Then, when the listener detects that the player’s left mouse button is within the radius of the grid, the position is captured. If the position has not been placed before, the player succeeds in landing, otherwise the player waits for the left mouse button to click again. If the game is over, the end of the game interface will be displayed. Otherwise, it will be the turn of THE AI. The LOGIC of the AI drop is similar to the player drop, and then the player drop, and so on.
Need to be aware that in order to ensure the real-time response, AI move later algorithm should be wrote after the mouse the left key click release events in the response (interested friends can try wrote the mouse to click in the response of events, which can cause must be at the end of the AI calculation and move later, can show the player of the last move later and AI the move later results).
The start button is just to reset the game, so there’s nothing to say about it, but in case some of you want to cheat, the code I implemented says you have to finish the current game to reset the game.
Since it’s against the AI, the retract button directly regrets two moves, pops the last two moves from the history list and then removes them from the corresponding position on the board, and ensures that only our own turn can retract to avoid unexpected logical errors.
The defeat button doesn’t have anything to say, it just throws in the towel and ends the game early.
Next we will implement the online play, here we choose to use TCP/IP protocol for online communication to achieve the online play. Start the game first on one side as the server side:
Thread(target=self.startListen).start()Start listening for client connections
def startListen(self) :
while True:
self.setWindowTitle('Gobang -🛰️: ilove-python -- > Server started successfully, waiting for client to connect')
self.tcp_socket, self.client_ipport = self.tcp_server.accept()
self.setWindowTitle('Gobang -🛰️: ilove- Python -- > Client connected, click start button to play')
Copy the code
The later initiator connects to the server as a 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 -- > Connect server successfully, click start button to play')
Copy the code
When the client connects to the server, the server also sends the server player basic information to the client:
data = {'type': 'nickname'.'data': self.nickname}
self.tcp_socket.sendall(packSocketData(data))
Copy the code
Then both the client and server use the newly opened thread to implement network data listening and receiving:
Receiving 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)
Copy the code
And make corresponding responses in the main process according to the different data received:
Response to received data.
def responseForReceiveData(self, data) :
if data['type'] = ='action' and data['detail'] = ='exit':
QMessageBox.information(self, 'tip'.'Your opponent has quit the game, the game will automatically return to the main screen')
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, 'tip'.'The other party requests to (re) start the game, you are %s, do you agree? ' % {'white': '白子'.'black': 'the spots'}.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('Gobackgammon -🛰️: ilove-python -- > %s moves' % 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
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 drop sounds
self.drop_sound.play()
The # final drop position flag follows the drop position
self.chessman_sign.show()
self.chessman_sign.move(c.pos())
self.chessman_sign.raise_()
Record this drop
self.history_record.append([*pos, self.whoseround])
# Victory or not
self.winner = checkWin(self.chessboard)
if self.winner:
self.showGameEndInfo()
return
# Change the color of the turn
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, 'tip'.'The other side requests to take back the chess, 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('Gobackgammon -🛰️: ilove-python -- > %s moves' % 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, 'tip'.'The other party agrees to start the game request, you are %s, the white person first.' % {'white': '白子'.'black': 'the spots'}.get(self.player_color))
else:
QMessageBox.information(self, 'tip'.'Your request to start the game has been rejected.')
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, 'tip'.'Your request for a reversal has been granted.')
else:
QMessageBox.information(self, 'tip'.'The opponent has rejected your request for a return.')
elif data['type'] = ='nickname':
self.opponent_nickname = data['data']
Copy the code
Modifications
Must click the “start” button, and after the other side agreed, can officially start the game, the reverse button can only be pressed in the other side’s turn, after the other side agreed to reverse, need to remember to switch back to his position. Then I added an urge button, which also had to be pressed during the opponent’s turn. So that’s all the code changes.
This is the end of the article, thank you for watching Python24 small games series, the next article to share 2048 small games
To thank the readers, I’d like to share some of my recent programming gems to give back to each and every one of you.
All done~ private message to get complete source code.
Review past
Python to achieve bomb people small game
Python to achieve classic pac-man games
Python to achieve elimination of music games
Python real dinosaur jump a jump small game is
Python simple version of the aircraft war games
Python tetris small game implementation
Python alien invasion to achieve small games
Python to achieve “little rabbit and Bun” game
Python eight notes to achieve small games
Python to achieve the puzzle game
Python to achieve ski games
Python for classic 90 tank wars
Python FlappyBird implementation of small games
Python to achieve tower defense games
Python to achieve fruit and gold coins small games
Python to achieve a small game push box
Python to achieve 24 small games
Python to achieve table tennis games
Python brick to achieve small games
Python to achieve a maze of small games
Python to achieve whack-a-mole games