Skip to content

Latest commit

 

History

History
299 lines (249 loc) · 11.1 KB

Caro-LAN-Python.md

File metadata and controls

299 lines (249 loc) · 11.1 KB

Game Cờ Caro Qua Mạng LAN Bằng Python

Cờ Caro là một Game trí tuệ đã rất quen thuộc với mỗi chúng ta. Với lối chơi đơn giản nhưng yếu tố trí tuệ lại rất cao nên cờ Caro được rất nhiều người yêu thích đặc biệt là các bạn học sinh, sinh viên và dân văn phòng. Game cờ Caro không chỉ mang tính chất giải trí đơn thuần mà là một cuộc đấu trí vô cùng gay cấn.

Hãy cùng mình làm một chương trình chơi cờ Caro đơn giản để cùng chơi với bạn bè của mình nhé.

Thuật ngữ và cách chơi trong Game

Thuật ngữ

  • Quân cờ: trong game sử dụng bộ dấu ký hiệu X/O

  • Bàn cờ Caro

    • Sử dụng bàn cờ kích thước X×Y (ô vuông)
    • Trên một bàn cờ chỉ cho phép 2 người tham gia.
  • Lượt chơi (tick X/O)

    • Một ván cờ có thể gồm nhiều lượt chơi.
    • Mỗi lượt người chơi thực hiện tick các dấu X/O vào hình vuông trên bàn cờ.

Luật chơi

Luật chơi cũng như cách chơi cờ caro hết sức đơn giản, hai bên thay phiên nhau tích vào những ô vuông trên bàn cờ, để tạo thành đường 5 quân theo hàng dọc, hàng ngang hoặc đường chéo là thắng. Tuy nhiên, nếu muốn chiến thắng khi chơi cờ caro bạn phải tạo ra những thế cờ độc, dùng chiến thuật và kinh nghiệm để đánh lừa và hạ gục đối thủ.

Bài viết mặc định yêu cầu bạn đã có kiến thức về Python cơ bản nhé

Cài đặt Python và thư viện cần thiết.

  • Cài đặt python :

Các bạn click vào đây để được hướng dẫn cài đặt Python

  • Cài đặt thư viện cho Python
pip install tk

Bắt đầu lập trình thôi nào.

Import các thư viện cần thiết

Mình dùng các thư viện tkinter, threading, socket để tạo giao diện, xử lý đa luồng và kết nối hai chương trình với nhau.

import tkinter as tk
from functools import partial
import threading
import socket
from tkinter import messagebox

Tạo giao diện cho chương trình

Mình tạo khởi tạo một lớp Window kế thừa từ tk.TK.

class Window(tk.Tk):

Hàm khởi tạo và tạo các thành phần trong GUI như Frame, Button, Label,....

def __init__(self):
        super().__init__()
        self.title("Caro Tkinter - codelearn.io")
        self.Buts = {}
        self.memory = []
        self.Threading_socket = Threading_socket(self)
        print(self.Threading_socket.name)

    def showFrame(self):
        frame1 = tk.Frame(self)
        frame1.pack()
        frame2 = tk.Frame(self)
        frame2.pack()

        Undo = tk.Button(frame1, text="Undo", width=10,  # nút quay lại
                         command=partial(self.Undo, synchronized=True))
        Undo.grid(row=0, column=0, padx=30)

        tk.Label(frame1, text="IP", pady=4).grid(row=0, column=1)
        inputIp = tk.Entry(frame1, width=20)  # Khung nhập địa chỉ ip
        inputIp.grid(row=0, column=2, padx=5)
        connectBT = tk.Button(frame1, text="Connect", width=10,
                              command=lambda: self.Threading_socket.clientAction(inputIp.get()))
        connectBT.grid(row=0, column=3, padx=3)

        makeHostBT = tk.Button(frame1, text="MakeHost", width=10,  # nút tạo host
                               command=lambda: self.Threading_socket.serverAction())
        makeHostBT.grid(row=0, column=4, padx=30)
        for x in range(Ox):   # tạo ma trận button Ox * Oy
            for y in range(Oy):
                self.Buts[x, y] = tk.Button(frame2, font=('arial', 15, 'bold'), height=1, width=2,
                                            borderwidth=2, command=partial(self.handleButton, x=x, y=y))
                self.Buts[x, y].grid(row=x, column=y)

Chúng ta tạo các hàm xử lý Button, kiểm tra người chiến thắng, thông báo và quay lại nước đánh trước.

def handleButton(self, x, y):
        if self.Buts[x, y]['text'] == "": #Kiểm tra ô có ký tự rỗng hay không
            if self.memory.count([x, y]) == 0:
                self.memory.append([x, y])
            if len(self.memory) % 2 == 1:
                self.Buts[x, y]['text'] = 'O'
                self.Threading_socket.sendData("{}|{}|{}|".format("hit", x, y))
                if(self.checkWin(x, y, "O")):
                    self.notification("Winner", "O")
                    self.newGame()
            else:
                print(self.Threading_socket.name)
                self.Buts[x, y]['text'] = 'X'
                self.Threading_socket.sendData("{}|{}|{}|".format("hit", x, y))
                if(self.checkWin(x, y, "X")):
                    self.notification("Winner", "X")
                    self.newGame()

    def notification(self, title, msg):
        messagebox.showinfo(str(title), str(msg))

    def checkWin(self, x, y, XO):
        count = 0
        i, j = x, y
        while(j < Ox and self.Buts[i, j]["text"] == XO):
            count += 1
            j += 1
        j = y
        while(j >= 0 and self.Buts[i, j]["text"] == XO):
            count += 1
            j -= 1
        if count >= 6:
            return True
        # check cột
        count = 0
        i, j = x, y
        while(i < Oy and self.Buts[i, j]["text"] == XO):
            count += 1
            i += 1
        i = x
        while(i >= 0 and self.Buts[i, j]["text"] == XO):
            count += 1
            i -= 1
        if count >= 6:
            return True
        # check cheo phai
        count = 0
        i, j = x, y
        while(i >= 0 and j < Ox and self.Buts[i, j]["text"] == XO):
            count += 1
            i -= 1
            j += 1
        i, j = x, y
        while(i <= Oy and j >= 0 and self.Buts[i, j]["text"] == XO):
            count += 1
            i += 1
            j -= 1
        if count >= 6:
            return True
        # check cheo trai
        count = 0
        i, j = x, y
        while(i < Ox and j < Oy and self.Buts[i, j]["text"] == XO):
            count += 1
            i += 1
            j += 1
        i, j = x, y
        while(i >= 0 and j >= 0 and self.Buts[i, j]["text"] == XO):
            count += 1
            i -= 1
            j -= 1
        if count >= 6:
            return True
        return False

    def Undo(self, synchronized):
        if(len(self.memory) > 0):
            x = self.memory[len(self.memory) - 1][0]
            y = self.memory[len(self.memory) - 1][1]
            # print(x,y)
            self.Buts[x, y]['text'] = ""
            self.memory.pop()
            if synchronized == True:
                self.Threading_socket.sendData("{}|".format("Undo"))
            print(self.memory)
        else:
            print("No character")

    def newGame(self):
        for x in range(Ox):
            for y in range(Oy):
                self.Buts[x, y]["text"] = ""

Tạo lớp đồng bộ dữ liều người chơi giữa hai chương trình :

Khởi tạo Class Threading_socket.

class Threading_socket():

Hàm clientActionserverAction sử dụng socket gửi và nhận dữ liệu qua lại giữa hai chương trình.

def __init__(self, gui):
        super().__init__()
        self.dataReceive = ""
        self.conn = None
        self.gui = gui
        self.name = ""

    def clientAction(self, inputIP):
        self.name = "client"
        print("client connect ...............")
        HOST = inputIP  # Cấu hình địa chỉ server
        PORT = 8000              # Cấu hình Port sử dụng
        self.conn = socket.socket(
            socket.AF_INET, socket.SOCK_STREAM)  # Cấu hình socket
        self.conn.connect((HOST, PORT))  # tiến hành kết nối đến server
        self.gui.notification("Đã kết nối tới", str(HOST))
        t1 = threading.Thread(target=self.client)  # tạo luồng chạy client
        t1.start()

    def client(self):
        while True:
            self.dataReceive = self.conn.recv(
                1024).decode()  # Đọc dữ liệu server trả về
            if(self.dataReceive != ""):
                friend = self.dataReceive.split("|")[0]
                action = self.dataReceive.split("|")[1]
                if(action == "hit" and friend == "server"):
                    #     print(self.dataReceive)
                    x = int(self.dataReceive.split("|")[2])
                    y = int(self.dataReceive.split("|")[3])
                    self.gui.handleButton(x, y)
                if(action == "Undo" and friend == "server"):

                    self.gui.Undo(False)
            self.dataReceive = ""

    def serverAction(self):
        self.name = "server"
        HOST = socket.gethostbyname(socket.gethostname())  # Láy  lập địa chỉ
        print("Make host.........." + HOST)
        self.gui.notification("Gui IP chp ban", str(HOST))
        PORT = 8000  # Thiết lập port lắng nghe
        # cấu hình kết nối
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind((HOST, PORT))  # lắng nghe
        s.listen(1)  # thiết lập tối ta 1 kết nối đồng thời
        self.conn, addr = s.accept()  # chấp nhận kết nối và trả về thông số
        t2 = threading.Thread(target=self.server, args=(addr, s))
        t2.start()

    def server(self, addr, s):
        try:
            # in ra thông địa chỉ của client
            print('Connected by', addr)
            while True:
                # Đọc nội dung client gửi đến
                self.dataReceive = self.conn.recv(1024).decode()
                if(self.dataReceive != ""):
                    friend = self.dataReceive.split("|")[0]
                    action = self.dataReceive.split("|")[1]
                    print(action)
                    if(action == "hit" and friend == "client"):
                        x = int(self.dataReceive.split("|")[2])
                        y = int(self.dataReceive.split("|")[3])
                        self.gui.handleButton(x, y)
                    if(action == "Undo" and friend == "client"):
                        self.gui.Undo(False)
                self.dataReceive = ""
        finally:
            s.close()  # đóng socket

    def sendData(self, data):
        # Gửi dữ liệu lên server`
        self.conn.sendall(str("{}|".format(self.name) + data).encode())

Khởi tạo hàm main và chạy chương trình

if __name__ == "__main__":
    Ox = 20  # Số lượng ô theo trục X
    Oy = 20 # Số lượng ô theo trục Y
    window = Window()
    window.showFrame()
    window.mainloop()

Kết

Bạn có thể tải source code trên tại đây

Trên đây là bài hướng dẫn lập trình Caro hai người chơi qua mạng Lan bằng Python. Rất mong sự ủng hộ của các bạn, nếu các bạn thấy bài viết có giá trị thì hãy share để ủng hộ mình nhé.