Software-Dev-Project / Website / app.py
app.py
Raw
from flask import Flask, render_template, request, redirect
from flask_socketio import SocketIO, join_room, leave_room, emit
from dbconnection import create_connection
import random
import string
import json
import uuid


app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret'
socketio = SocketIO(app)

cards = [
    {
        "name": 'Plain',
        "value": 1,
        "number": 5
    },
    {
        "name": "Powdered",
        "value": 2,
        "number": 2
    },
    {
        "name": "Chocolate Sprinkle",
        "value": 3,
        "number": 2
    },
    {
        "name": "Bear Claw",
        "value": 7,
        "number": 1
    },
    {
        "name": "Cinnamon Bun",
        "value": 4,
        "number": 2
    },
    {
        "name": "Boston Cream",
        "value": 6,
        "number": 1
    },
    {
        "name": "Jelly Filled",
        "value": 5,
        "number": 2
    },
    {
        "name": "Apple Fritter",
        "value": 8,
        "number": 1
    }
]

def createDeck():
    deck = []
    for card in cards:
        number = card['number']
        while number != 0:
            deck.append(card)
            number -= 1
    random.shuffle(deck)
    return deck

def initializeTable():
    connection = create_connection()

    if connection is not None:
        print("Connected to the database!")
        cur = connection.cursor()
        cur.execute('''
            CREATE TABLE IF NOT EXISTS ActiveSessions (
                RoomID VARCHAR(10) PRIMARY KEY NOT NULL,
                RoomTitle TEXT NOT NULL,
                HostName VARCHAR (40) NOT NULL,
                Players INTEGER NOT NULL
            )
        ''')

        cur.execute('''
            CREATE TABLE IF NOT EXISTS PlayerRosters (
                RoomID VARCHAR(10) NOT NULL,
                Player1 JSON,
                Player2 JSON,
                Player3 JSON,
                Player4 JSON,
                FOREIGN KEY (RoomID) REFERENCES ActiveSessions (RoomID)
            )         
        ''')

        cur.execute('''
            CREATE TABLE IF NOT EXISTS GameHistory (
                RoomID VARCHAR(10) NOT NULL,
                RoomTitle TEXT NOT NULL,
                HostName VARCHAR (40) NOT NULL,
                Players INTEGER NOT NULL,
                Winner VARCHAR(40)
            )
        ''')

        connection.commit()
        connection.close()
    else:
        print("Failed to connect to the database.")

def generateRoomID(length = 8):
    characters = string.ascii_letters + string.digits
    room_id = ''.join(random.choice(characters) for _ in range(length))
    return room_id

def listAvailableRooms():
    connection = create_connection()
    cur = connection.cursor()
    cur.execute('SELECT * FROM ActiveSessions')
    activeSessions = cur.fetchall()

    listAvailableRooms = []

    for i in range(len(activeSessions)):
        cur.execute('''
        SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
        ''', (activeSessions[i][0],))
        record = cur.fetchone()
        if record is not None:
            players = [item for item in record if item is not None]
            if len(players) < activeSessions[i][-1]:
                listAvailableRooms.append({
                    "roomID": activeSessions[i][0],
                    "roomTitle": activeSessions[i][1],
                    "hostName": activeSessions[i][2],
                    "numPlayers": activeSessions[i][3],
                    "activeNums": len(players)
                })

    connection.commit()
    connection.close()
    return listAvailableRooms

def listHistory():
    connection = create_connection()
    cur = connection.cursor()

    cur.execute('''
    SELECT * FROM GameHistory
    WHERE WINNER IS NOT NULL
    ''')

    listHistoryRecords = cur.fetchall()

    connection.commit()
    connection.close()
    return listHistoryRecords

def addPlayer(roomID, playerName):
    player = {
        "playerID": str(uuid.uuid4()),
        "name": playerName,
        "roomID": roomID,
        "readyStatus": False,
        "hand": [],
        "discard": [],
        "token": 0,
        "isHost": False,
        "isMyTurn": False,
        "outOfRound": False,
        "invincible": False
    }
    return player

def createGameSession(roomTitle, hostName, numPlayers):
    connection = create_connection()
    roomID = generateRoomID()
    cur = connection.cursor()

    sessionRecord = '''
        INSERT INTO ActiveSessions(RoomID, RoomTitle, HostName, Players)
        VALUES (%s, %s, %s, %s)
    '''
    data = (roomID, roomTitle, hostName, numPlayers)
    cur.execute(sessionRecord, data)

    gameHistoryRecord = '''
        INSERT INTO GameHistory(RoomID, RoomTitle, HostName, Players)
        VALUES (%s, %s, %s, %s)
    '''
    data = (roomID, roomTitle, hostName, numPlayers)
    cur.execute(gameHistoryRecord, data)

    playerRecord = '''
    INSERT INTO PlayerRosters (RoomID, Player1)
    VALUES (%s, %s)
    '''
    player = {
        "playerID": str(uuid.uuid4()),
        "name": hostName,
        "roomID": roomID,
        "readyStatus": True,
        "hand": [],
        "discard": [],
        "token": 0,
        "isHost": True,
        "isMyTurn": False,
        "outOfRound": False,
        "invincible": False,
        "gameStatus": False
    }

    data = (roomID, json.dumps(player))
    cur.execute(playerRecord, data)
    connection.commit()
    connection.close()

    return roomID

def joinGameSession(roomID, playerName):
    connection = create_connection()
    cur = connection.cursor()

    cur.execute('''
    SELECT Players FROM ActiveSessions WHERE RoomID = %s
    ''', (roomID,))
    numPlayers = cur.fetchone()[0]
    print(numPlayers)

    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    record = cur.fetchone()

    if record is not None:
        players = [item for item in record if item is not None]

    if len(players) == numPlayers:
        print("asdfsdafsad")
        showAvailableRooms = listAvailableRooms()
        return render_template("sessionPage.html", message = "Sorry, The room is full!", data = showAvailableRooms)
    
    if players[0]["gameStatus"] == True:
        showAvailableRooms = listAvailableRooms()
        return render_template("sessionPage.html", message = "Sorry, The game has started!", data = showAvailableRooms)
    
    for i in range(1, numPlayers):
        if record[i] == None:
            playerRecord = '''
                UPDATE PlayerRosters
                SET {} = %s
                WHERE RoomID = %s
            '''.format("Player" + str(i + 1))
        
            player = addPlayer(roomID, playerName)

            data = (json.dumps(player), roomID)
            cur.execute(playerRecord, data)

            connection.commit()
            connection.close()
            break

    return redirect(f'/room/{roomID}')

def addGameRecord(roomID, playerName):
    connection = create_connection()
    cur = connection.cursor()
    cur.execute('''
        UPDATE GameHistory
        SET Winner = %s
        WHERE RoomID = %s
    ''', (playerName, roomID))
    
    connection.commit()
    connection.close()

initializeTable()

@app.route("/")
def landingPage():
    showHistory = listHistory()
    return render_template("landingPage.html", data = showHistory)

@app.route("/aboutus")
def aboutus():
    return render_template("aboutUs.html")


@app.route('/session', methods=['GET', 'POST'])
def gameSession():
    if request.method == "POST":
        playerName = request.form.get("name")
        numPlayers = request.form.get("mySelect")
        roomID = request.form.get("room-id")
        title = request.form.get("myTitle")

        if roomID is None:
            roomID = createGameSession(title, playerName, numPlayers)
            return redirect(f'/room/{roomID}')
        else:
            return joinGameSession(roomID, playerName)
    else:
        showAvailableRooms = listAvailableRooms()
        return render_template("sessionPage.html", data = showAvailableRooms )

@app.route("/room/<roomID>")
def room(roomID):
    connection = create_connection()
    cur = connection.cursor()
    cur.execute('SELECT * FROM ActiveSessions WHERE RoomID = %s', (roomID,))
    sessionRecord = cur.fetchone()
    (RoomID, RoomTitle, HostName, Players) = sessionRecord

    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    record = cur.fetchone()
    if record is not None:
        players = [item for item in record if item is not None]
    
    if sessionRecord is None:
        return render_template('404Page.html')
    
    data = {
        "roomID": RoomID,
        "roomTitle": RoomTitle,
        "hostName": HostName,
        "numPlayers": Players,
        "activeNums": len(players),
        "playerID": players[-1]["playerID"],
        "isHost": players[-1]["isHost"]
    }
    connection.close()
    return render_template("gamePage.html", data = data)


@app.errorhandler(404)
def pageNotFound(error):
    return render_template('404Page.html'), 404

@socketio.on("join")
def onJoin(data):
    roomID = data["roomID"]

    join_room(roomID)

    connection = create_connection()
    cur = connection.cursor()
    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    playerRecord = cur.fetchone()
    if playerRecord is not None:
        players = [item for item in playerRecord if item is not None]
    players[-1]["sid"] = request.sid
    cur.execute('''
        UPDATE PlayerRosters
        SET {} = %s
        WHERE RoomID = %s
        '''.format('Player'+ str(len(players))), (json.dumps(players[-1]), roomID))
    connection.commit()
        
    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    playerRecord = cur.fetchone()
    if playerRecord is not None:
        players = [item for item in playerRecord if item is not None]
    
    connection.close()
    emit('playerJoined', {'name': players[-1]["name"]}, room=roomID)
    emit('updatePlayerList', {'players': players}, room=roomID)

@socketio.on('ready')
def onReady(data):
    playerID = data["playerID"]
    roomID = data["roomID"]
    ready = data["readyStatus"]

    connection = create_connection()
    cur = connection.cursor()
    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    playerRecord = cur.fetchone()

    if playerRecord is not None:
        players = [item for item in playerRecord if item is not None]
    player = next((p for p in players if p['playerID'] == playerID), None)

    if player is not None:
        index = players.index(player) + 1
        player["readyStatus"] = ready
        cur.execute('''
        UPDATE PlayerRosters
        SET {} = %s
        WHERE RoomID = %s
        '''.format('Player'+ str(index)), (json.dumps(player), roomID))
        connection.commit()

    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    playerRecord = cur.fetchone()
    if playerRecord is not None:
        players = [item for item in playerRecord if item is not None]
    
    connection.close()
    emit('updatePlayerList', {'players': players}, room=roomID)

@socketio.on('startGame')
def startGame(data):
    roomID = data["roomID"]
    numPlayers = int(data["numPlayers"])
    
    connection = create_connection()
    cur = connection.cursor()
    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    playerRecord = cur.fetchone()
    if playerRecord is not None:
        players = [item for item in playerRecord if item is not None]

    allReady = all(p['readyStatus'] for p in players)

    if len(players) < numPlayers:
        emit('message', {'message': 'Not Enough Players!'}, room = roomID)
    elif allReady == False:
        emit('message', {
             'message': 'Not All Players Are Ready!'}, room = roomID)
    else:
        deck = createDeck()
        for player in players:
            player['hand'].append(deck.pop())

        outOfCards = []
        if len(players) == 2:
            for i in range(3):
                outOfCards.append(deck.pop())

        players[0]["deck"] = deck
        players[0]["gameStatus"] = True

        if len(deck) > 0 and len(players) > 1:
            index = random.randint(0, len(players) - 1)
            players[index]['isMyTurn'] = True

        for i in range(len(players)):
            cur.execute('''
            UPDATE PlayerRosters
            SET {} = %s
            WHERE RoomID = %s
            '''.format('Player'+ str(i + 1)), (json.dumps(players[i]), roomID))
        connection.commit()

        cur.execute('''
        SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
        ''', (roomID,))
        playerRecord = cur.fetchone()
        if playerRecord is not None:
            players = [item for item in playerRecord if item is not None]
        
        currentPlayer = players[index]
        connection.close()
        emit("startGame", {"players":  players, "outOfCards": outOfCards}, room = roomID)
        emit("yourTurn", {"player": currentPlayer}, room = roomID)

@socketio.on("nextRound")
def nextRound(data):
    players = data["players"]
    roomID = data["roomID"]

    for i in range(len(players)):
        players[i]["hand"] = []
        players[i]["discard"] = []
        players[i]["isMyTurn"] = False
        players[i]["outOfRound"] = False
        players[i]["invincible"] = False

    deck = createDeck()
    for player in players:
        player['hand'].append(deck.pop())

    outOfCards = []
    if len(players) == 2:
        for i in range(3):
            outOfCards.append(deck.pop())

    players[0]["deck"] = deck

    if len(deck) > 0 and len(players) > 1:
        index = random.randint(0, len(players) - 1)
        players[index]['isMyTurn'] = True

    connection = create_connection()
    cur = connection.cursor()

    for i in range(len(players)):
        cur.execute('''
        UPDATE PlayerRosters
        SET {} = %s
        WHERE RoomID = %s
        '''.format('Player'+ str(i + 1)), (json.dumps(players[i]), roomID))
    connection.commit()

    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    playerRecord = cur.fetchone()
    if playerRecord is not None:
        players = [item for item in playerRecord if item is not None]
    
    currentPlayer = players[index]
    connection.close()
    emit("startGame", {"players":  players, "outOfCards": outOfCards}, room = roomID)
    emit("yourTurn", {"player": currentPlayer}, room = roomID)


@socketio.on('drawCard')
def drawCard(data):
    currentPlayer = data["player"]
    roomID = data["roomID"]

    connection = create_connection()
    cur = connection.cursor()
    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    playerRecord = cur.fetchone()
    if playerRecord is not None:
        players = [item for item in playerRecord if item is not None]
    deck = players[0]["deck"]
    idx = 0
    for i, player in enumerate(players):
        if player.get('sid') == currentPlayer["sid"]:
            idx = i
            break
    if len(deck) > 0:
        players[idx]["hand"].append(deck.pop())
    players[0]["deck"] = deck
    cur.execute('''
    UPDATE PlayerRosters
    SET Player1 = %s
    WHERE RoomID = %s
    ''', (json.dumps(players[0]), roomID))

    cur.execute('''
    UPDATE PlayerRosters
    SET {} = %s
    WHERE RoomID = %s
    '''.format('Player'+ str(idx + 1)), (json.dumps(players[idx]), roomID))
    connection.commit()

    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    playerRecord = cur.fetchone()
    if playerRecord is not None:
        players = [item for item in playerRecord if item is not None]

    connection.close()
    emit('updateGameStatus', {"players": players}, room=roomID)

@socketio.on('playcard')
def playCard(data):
    roomID = data["roomID"]
    playerIndex = data["playerIndex"]
    players = data["players"]
    
    deck = players[0]["deck"]
    for i in range(len(players)):
        if len(players[i]["hand"]) == 0 and len(deck) > 0:
            players[i]["hand"].append(deck.pop())
    players[0]["deck"] = deck

    connection = create_connection()
    cur = connection.cursor()
    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    playerRecord = cur.fetchone()
    if playerRecord is not None:
        newPlayers = [item for item in playerRecord if item is not None]
    players[playerIndex]["isMyTurn"] = False  

    nextPlayerIndex = (playerIndex + 1) % len(newPlayers)
    
    while (players[nextPlayerIndex]["outOfRound"]):
         nextPlayerIndex = (nextPlayerIndex + 1) % len(newPlayers)

    players[nextPlayerIndex]["isMyTurn"] = True
    if players[nextPlayerIndex]["invincible"] == True:
        players[nextPlayerIndex]["invincible"] = False

    if len(players) == 2:
        if players[0]['outOfRound'] == True:
            players[1]["token"] = players[1]["token"] + 1
        elif players[1]["outOfRound"] == True:
            players[0]["token"] = players[0]["token"] + 1

    if len(players) == 3:
        outPlayer = list(filter(lambda player: player["outOfRound"] == True, players))
        winner = list(filter(lambda player: player["outOfRound"] == False, players))
        if len(outPlayer) == 2 and len(winner) == 1:
            for i in range(len(players)):
                if players[i]["playerID"] == winner[0]["playerID"]:
                    players[i]["token"] = players[i]["token"] + 1

    if len(players) == 4:
        outPlayer = list(filter(lambda player: player["outOfRound"] == True, players))
        winner = list(filter(lambda player: player["outOfRound"] == False, players))
        if len(outPlayer) == 3 and len(winner) == 1:
            for i in range(len(players)):
                if players[i]["playerID"] == winner[0]["playerID"]:
                    players[i]["token"] = players[i]["token"] + 1

    for i in range(len(newPlayers)):
        cur.execute('''
        UPDATE PlayerRosters
        SET {} = %s
        WHERE RoomID = %s
        '''.format('Player'+ str(i + 1)), (json.dumps(players[i]), roomID))

    connection.commit()

    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    playerRecord = cur.fetchone()
    if playerRecord is not None:
        players = [item for item in playerRecord if item is not None]

    
    emit("updateGameStatus", {"players": players}, room=roomID)

    if len(players) == 2:
        if players[0]["token"] == 7:
            addGameRecord(roomID, players[0]["name"])
            socketio.emit("message", { "message": "Congratulations, {} Wins the Game".format(players[0]["name"])}, room = roomID)
            return
        elif players[1]["token"] == 7:
            addGameRecord(roomID, players[1]["name"])
            socketio.emit("message", { "message": "Congratulations, {} Wins the Game".format(players[1]["name"])}, room = roomID)
            return
        elif players[0]['outOfRound'] == True:
            socketio.emit("message", { "message": "Congratulations, {} Wins the Round".format(players[1]["name"]), "players": players}, room = roomID)
            return
        elif players[1]["outOfRound"] == True:
            socketio.emit("message", { "message": "Congratulations, {} Wins the Round".format(players[0]["name"]), "players": players}, room = roomID)
            return
        
    if len(players) == 3:
        winner = list(filter(lambda player: player["token"] == 5, players))
        victor = list(filter(lambda player: player["outOfRound"] == False, players))
        if len(winner) == 1:
            if winner[0]["token"] == 5:
                addGameRecord(roomID, winner[0]["name"])
                socketio.emit("message", { "message": "Congratulations, {} Wins the Game".format(winner[0]["name"])}, room = roomID)
                return
        if len(victor) == 1:
            socketio.emit("message", { "message": "Congratulations, {} Wins the Round".format(victor[0]["name"]), "players": players}, room = roomID)
            return
        
    if len(players) == 4:
        winner = list(filter(lambda player: player["token"] == 4, players))
        victor = list(filter(lambda player: player["outOfRound"] == False, players))
        if len(winner) == 1:
            if winner[0]["token"] == 4:
                addGameRecord(roomID, winner[0]["name"])
                socketio.emit("message", { "message": "Congratulations, {} Wins the Game".format(winner[0]["name"])}, room = roomID)
                return
        if len(victor) == 1:
            socketio.emit("message", { "message": "Congratulations, {} Wins the Round".format(victor[0]["name"]), "players": players}, room = roomID)
            return
    
    connection.close()
    emit('yourTurn', {"player": players[nextPlayerIndex]}, room=roomID)

@socketio.on('leave')
def onLeave(data):
    roomID = data["roomID"]
    sid = data["sid"]

    leave_room(roomID)

    leftPlayer = None
    connection = create_connection()
    cur = connection.cursor()
    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    playerRecord = cur.fetchone()
    if playerRecord is not None:
        players = [item for item in playerRecord if item is not None]

    for i in range(len(players)):
        if players[i]["sid"] == sid:
             leftPlayer = players[i]
             cur.execute('''
                UPDATE PlayerRosters
                SET {} = %s
                WHERE RoomID = %s
                '''.format('Player'+ str(i + 1)), (None, roomID))
             connection.commit()
             break
    
    cur.execute('''
    SELECT Player1, Player2, Player3, Player4 FROM PlayerRosters WHERE RoomID = %s
    ''', (roomID,))
    newRecord = cur.fetchone()
    if playerRecord is not None:
        updatedPlayers = [item for item in newRecord if item is not None]

    if len(updatedPlayers) == 0:
        cur.execute('''
        DELETE FROM PlayerRosters
        WHERE RoomID = %s            
        ''', (roomID,))
        cur.execute('''
        DELETE FROM ActiveSessions
        WHERE RoomID = %s            
        ''', (roomID,))
        connection.commit()
    connection.close()
    emit('playerLeft', {'name': leftPlayer["name"]}, room = roomID)
    emit('updatePlayerList', {'players': updatedPlayers}, room = roomID)

@socketio.on('disconnect')
def handle_disconnect():
    print('Client disconnected')

if __name__ == '__main__':
    socketio.run(app)