Python Multiplayer noughts and crosses

786 views Asked by At

So I made a single player noughts and crosses in Python a little while ago in school.

score=[[' ',' ',' '],[' ',' ',' '],[' ',' ',' ']]
global attempts
attempts = 0
from random import randint
from time import sleep

def grid(): #draws playing grid
    hideturtle()
    speed(0)
    pensize(0)
    penup()
    setpos(-200, -67)
    pendown()
    fd(400)
    penup()
    setpos(-200, 66)
    pendown()
    fd(400)
    penup()
    seth(90)
    setpos(-67, -200)
    pendown()
    fd(400)
    penup()
    setpos(66, -200)
    pendown()
    fd(400)

def drawShape(s, x, y): #draws shape in grid box x and y = coord, s = shape type
    hideturtle()
    speed(100)
    pensize(6)
    penup()
    if s == 'X': #draws 'X'
        pencolor("orange")
        setpos(-266+(133*x), -266+(133*y))
        pendown()
        seth(135)
        fd(50)
        rt(180)
        fd(100)
        rt(180)
        fd(50)
        rt(90)
        fd(50)
        rt(180)
        fd(100) 
    elif s == 'O': #draws 'O'
        pencolor("green")
        setpos(-266+(133*x), -266+(133*y)-40)
        seth(0)
        pendown()
        circle(40)



def ai(): #EXPERIMENTAL AI
    x=0
    y=0
    d='O'
    e='O'
    f='O'
    for i in range(3): #checks positions
        if i == 0:
            d=' '
        elif i == 1:
            d='O'
            e=' '
        elif i == 2:
            d='O'
            e='O'
            f=' '
        for c in range(3):
            if score[c][0] == d and score[c][1] == e and score[c][2] == f:
                x = c+1
                y = i+1
                print('v',c)
            elif score[c][0] == d and score[c][1] == e and score[c][2] == f:
                x = i=1
                y = c+1
                print('h',c)
        if score[0][0] == d and score[1][1] == e and score[2][2] == f:
            print('lr',i)
            x = i+1
            y = i+1
        elif score[0][2] == d and score[1][1] == e and score[2][0] == f:
            print('rl',i)
            x = i+1
            y = 4-i
    d='X'
    e='X'
    f='X'
    if x == 0 and y == 0: #checks oposition positions
        for i in range(3):
            if i == 0:
                d=' '
            elif i == 1:
                d='X'
                e=' '
            elif i == 2:
                d='X'
                e='X'
                f=' '
            for c in range(3): 
                if score[c][0] == d and score[c][1] == e and score[c][2] == f:
                    x = c+1
                    y = i+1
                    print('op v')
                elif score[c][0] == d and score[c][1] == e and score[c][2] == f:
                    x = i=1
                    y = c+1
                    print('op v')
            if score[0][0] == d and score[1][1] == e and score[2][2] == f:
                x = i+1
                y = i+1
                print('op bt')
            elif score[0][2] == d and score[1][1] == e and score[2][0] == f:
                x = i+1
                y = 4-i
                print('op tb')

    if x == 0 and y == 0: #if no playable positions uses random
        x = randint(1,3)
        y = randint(1,3)
    return x, y

def valid(u,x,y): #checks player move is valid
    global attempts
    if x > 3 or y > 3:
        print ('Coordinate must be between 1 & 3')
    elif x == '' or y == '':
        print("Enter something!")
    elif score[y-1][x-1] == ' ':
        score[y-1][x-1] = u
        drawShape(u, x, y)
        attempts +=1
        return True
    elif score[y-1][x-1] == u:
        print("You've already gone here! ")
        return False
    elif score[y-1][x-1] != u:
        print("The other player is here! ")
        return False


def userAgent(u): #makes AI or user prompts and sets array
    global attempts
    global a
    global b
    if u == 0:
        a, b = ai()
        score[b-1][a-1] = 'O'
        print("The computer is taking its turn...")
        print(a,b)
        sleep(1)
        drawShape('O', a, b)
        attempts +=1
    else:
        x = input("Player "+u+": enter x coordinate (1-3) ")
        y = input("Player "+u+": enter y coordinate (1-3) ")
        try:
            x = int(x)
            y = int(y)
        except ValueError:
            print("That's not a valid number!")
            userAgent(u)
        while True:
            if valid(u,x,y) == True:
                break
            x = input("Player "+u+": enter x coordinate (1-3) ")
            y = input("Player "+u+": enter y coordinate (1-3) ")
            try:
                x = int(x)
                y = int(y)
            except ValueError:
                print("That's not a valid number!")


def checkWin(n): #checks for a player win (3 in row) or stalemate
    for i in range(3):
        if score[i][0] == n and score[i][1] == n and score[i][2] == n:
            print("Player "+n+" won!")
            return True
        elif score[0][i] == n and score[1][i] == n and score[2][i] == n:
            print("Player "+n+" won!")
            return True
    if score[0][0] == n and score[1][1] == n and score[2][2] == n:
        print("Player "+n+" won!")
        return True
    elif score[0][2] == n and score[1][1] == n and score[2][0] == n:
        print("Player "+n+" won!")
        return True
    elif attempts == 9:
        print("Stalemate!")
        return True
    else:
        return False

def printGrid():
    print(score[2])
    print(score[1])
    print(score[0])

from turtle import *
grid()
p = input("Are you playing by yourself? (SINGLE PLAYER EXPERIMENTAL) (y/n) ")

while True: #runs game until player win
    if p == 'y':
        userAgent('X')
        printGrid()
        if checkWin('X') == True:
            break
        userAgent(0)
        printGrid()
        if checkWin('O') == True:
            break

    elif p == 'n':
        userAgent('X')

        if checkWin('X') == True:
            break
        userAgent('O')

        if checkWin('O') == True:
            break

    else:
        print("You need to type y or n - try again!")
        p = input("Are you playing by yourself? (SINGLE PLAYER EXPERIMENTAL) (y/n) ")

input('Press ENTER to exit')

Just ignore that AI function it's now permanently experimental (doesn't work) but that's not the problem.

I'm helping out at my school's open evening and I thought it'd be cool if 2 people could play against each other on different computers. So I'd host the server on my home PC with port forwarding, doing all the logic, and the clients would just take inputs, send them onto the server and draw moves by both players. I know HTTP POST/GET, but how would I get the server to tell the client the other player moved? I've looked into Twisted and it seems good, but I really don't understand classes (I've only been programming for a little while).

This would be on a school computer so I have no access to port forwarding for the client. Ideally I'd also have an IP whitelist so only the computer's I want can access the server....

Soooo can anyone help me out here? I just need to understand what code I'd need for the server and how the client would interact with it. Thanks :)

3

There are 3 answers

0
John La Rooy On BEST ANSWER

You can start from the twisted chatserver example

"""The most basic chat protocol possible.

run me with twistd -y chatserver.py, and then connect with multiple
telnet clients to port 1025
"""

from twisted.protocols import basic



class MyChat(basic.LineReceiver):
    def connectionMade(self):
        print "Got new client!"
        self.factory.clients.append(self)

    def connectionLost(self, reason):
        print "Lost a client!"
        self.factory.clients.remove(self)

    def lineReceived(self, line):
        print "received", repr(line)
        for c in self.factory.clients:
            c.message(line)

    def message(self, message):
        self.transport.write(message + '\n')


from twisted.internet import protocol
from twisted.application import service, internet

factory = protocol.ServerFactory()
factory.protocol = MyChat
factory.clients = []

application = service.Application("chatserver")
internet.TCPServer(1025, factory).setServiceParent(application)

The clients can use Python's builtin telnetlib to connect

Later on you can upgrade to using twisted for the client connections if you like.

2
John On

The easiest way to have both clients interact through a server is to have both clients poll the server for updates. (you can do server side events, but that is way too complicated for what you are looking for).

However, if they are within the same network I recommend opening a socket server and client on each device directly communicating.

The way I would do it is to this:

  1. Sockets servers are opened on both machines
  2. Whoever starts the game opens a socket client and sends a the move directly to server, then reopens a socket server
  3. The next move the computer opens a client and sends a move back to server.

This takes out all the unnecessary code for running a server.

See the python manual for sockets. There are great examples at the bottom for both server/client side.

https://docs.python.org/2/library/socket.html

0
Will Harris On

You might want to take a look at ZeroMQ. http://zeromq.org/

Specifically, you'd want PyZMQ. http://zeromq.github.io/pyzmq/api/zmq.html

It's simple to set up, and you can connect the two computers directly. You'd be able to send messages back and forth between the two.

Definitely read the docs, but you're probably going to want a Request/Reply pattern.