Premise of our Project:

  • Website that has a connection to different games (Game Hub, like math.com or something)
    • Include games like:
      • Pacman
      • Guessing games
      • Clicking game (cookie clicker) + (maybe) gacha
      • Snake
      • Aim Trainer (That we will make)
      • Binary game
      • Akinator (AI)???

Incorperating Aspects of Unit 2:

  • Stores your highscores for games like: Pacman, Guessing Game, Clicking Game, Snake
    • Stores other stats like how many times you’ve died, how many answers you’ve gotten wrong
    • If we get the gacha game working, the game can store your gacha and what you have
    • (Data Storage)
  • Data Collection and usage
    • In each game that is possible to have a highscore, we will have a leaderboard of all the users that have played and scored points on the game. It will be organized from the highest scoring to the lowest scoring. The average score will be given as well.
    • Ghost (Highscore player, retrace their steps and play it back?)
  • Store games on the backend
    • Scores are able to be tampered with through inspect, store scores in backend?

How to get it done:

  • Frontend: We plan to create every game from scratch in python, including the aim trainer and the (maybe) gacha.
    • For the aim rainer, we imagined that it would be a simple aim trainer with easy, medium, and hard mode. The easy mode would have the dots that you click very clode together, the medium would speed up the pace that the dots dissappear and appear at, then finally, the hard mode would have the dots very far away from each other and they will dissappear very fast.
    • For the guessing game, we decided that we did something unique. We would have an x on a number line and the user would try to guess where the x is placed.
    • Pacman should be relativley the same, there are entities that chase you while you try to collect all the points
    • Snake will also be relaivley the same. There is a snake and it will try to eat all the apples.
    • For the clicker game, we want to either make it a gacha game or keep it like cookie clicker. For the gacha approach, we wanted to have it to each click would rack up points, and after a while the points can be used to roll a gacha. There would be 10 different collectable animals. If we decided not to impliment the gacha part, we would just make it so that you can click a button and earn points.
    • Binary game would be a timed game where you are given a number and a row of binary where you can click to turn the row of zeros into either 1’s or 0’s, and based off of the number you are given you have to change the 0s and 1s to match the number. Also, you could be given a row of binary and asked what number it is.
  • Backend: We plan to have personal accounts for each person so that all their data is stored (their highscores, which games they’ve played, how long they’ve played, what gacha’s they’ve obtained)
    • Also, some games developed from backend would be maybe the Akinator game (if we can get AI to work) as well as an image adjuster program.

Outline of Binary Code:

Working Binary Code from the Passion Project:

Below is code from my passion project for the binary game that I made.

<!DOCTYPE html>
<html>
<head>
    <title>Binary Game</title>
    <style>
        /*Buttons beside start don't appear until start is clicked*/
        .binary-button {
            display: none;
            width: 100px; /* Set the width of the buttons */
            height: 40px; /* Set the height of the buttons */
            font-size: 16px; /* Set the font size of the buttons */
        }
        #check {
            display: none;
        }
        #next {
            display: none;
        }
        #tryAgain {
            display: none;
        }
    </style>
</head>
<body>
    <h1 >Binary Game</h1>
    <p id="directions">Click start to begin!</p>
    <p id="randomNumber"></p>
    <div id="starting">
        <button id="start" onclick="start()">Start!</button>
    </div>
    <p id="sc"></p>
    <p id="attempt"></p>
    <button id="tryAgain" onclick="restart()">Try Again!</button>
    <div id="binary-buttons">
        <button class="binary-button" id="b5" onclick="c5()">0</button>
        <button class="binary-button" id="b4" onclick="c4()">0</button>
        <button class="binary-button" id="b3" onclick="c3()">0</button>
        <button class="binary-button" id="b2" onclick="c2()">0</button>
        <button class="binary-button" id="b1" onclick="c1()">0</button>
        <button class="binary-button" id="b0" onclick="c0()">0</button>
    </div>
    <br>
    <button id="check" onclick="check()">Check!</button>
    <button id="next" onclick="moveon()">Next!</button>
    <p id="para"></p>
</body>
<script>

    var randNumber = 0;
    var score = 0;
    var attempts = 2;



    function start() {
        var buttons = document.getElementById("binary-buttons")
        var start = document.getElementById("start");
        var check = document.getElementById("check");
        var text = document.getElementById("directions");
        var number = document.getElementById("randomNumber")
        var b5 = document.getElementById("b5");
        var b4 = document.getElementById("b4");
        var b3 = document.getElementById("b3");
        var b2 = document.getElementById("b2");
        var b1 = document.getElementById("b1");
        var b0 = document.getElementById("b0");
        var sc = document.getElementById("sc");
        var a = document.getElementById("attempt")

        check.style.display = "inline-block";
        text.innerHTML = "Click the boxes to match the number in binary!";
        start.style.display = "none"; // Hide the "Start" button
        b5.style.display = "inline-block"
        b4.style.display = "inline-block"
        b3.style.display = "inline-block"
        b2.style.display = "inline-block"
        b1.style.display = "inline-block"
        b0.style.display = "inline-block"
        randNumber = randnum()
        number.innerHTML = "Number: " + randNumber
        sc.innerHTML = "Score: " + score
        a.innerHTML = "Attemtps Left: " + (attempts + 1)
    }

    function randbin(num) {
        return (num >>> 0).toString(2);
    }

    function randnum() {
        number = Math.floor(Math.random() * 64);
        return number
    }

    function moveon() {
        var number = document.getElementById("randomNumber")
        document.getElementById("b0").innerHTML = "0"
        document.getElementById("b1").innerHTML = "0"
        document.getElementById("b2").innerHTML = "0"
        document.getElementById("b3").innerHTML = "0"
        document.getElementById("b4").innerHTML = "0"
        document.getElementById("b5").innerHTML = "0"
        document.getElementById("para").innerHTML = "";
        randNumber = randnum()
        number.innerHTML = "Number " + randNumber
    }

    // Define a common function for changing button text and style
    function changeText(buttonId, originalText) {
        var button = document.getElementById(buttonId);
        if (button.innerHTML === originalText) {
            button.innerHTML = "1";
        } else {
            button.innerHTML = originalText;
        }
    }

    function c5() {
        changeText("b5", "0");
    }

    function c4() {
        changeText("b4", "0");
    }

    function c3() {
        changeText("b3", "0");
    }

    function c2() {
        changeText("b2", "0");
    }

    function c1() {
        changeText("b1", "0");
    }

    function c0() {
        changeText("b0", "0");
    }

    function check() {
        var value = 0;

        if (document.getElementById("b5").innerHTML === "1") {
            value += 32;
        }

        if (document.getElementById("b4").innerHTML === "1") {
            value += 16;
        }

        if (document.getElementById("b3").innerHTML === "1") {
            value += 8;
        }

        if (document.getElementById("b2").innerHTML === "1") {
            value += 4;
        }

        if (document.getElementById("b1").innerHTML === "1") {
            value += 2;
        }

        if (document.getElementById("b0").innerHTML === "1") {
            value += 1;
        }

        if (value === randNumber) {
            var next = document.getElementById("next")
            var sc = document.getElementById("sc")
            next.style.display = "inline-block"
            document.getElementById("para").innerHTML = "Correct!";
            score += 1
            sc.innerHTML = "Score: " + score
        } 
        if (attempts == 0) {
            var a = document.getElementById("attempt")
            attempts -= 1
            a.innerHTML = "Attempts Left: " + (attempts + 1)
            gameover()
        }
        
        else {
            var a = document.getElementById("attempt")
            document.getElementById("para").innerHTML = "Incorrect! Try again.";
            document.getElementById("b0").innerHTML = "0"
            document.getElementById("b1").innerHTML = "0"
            document.getElementById("b2").innerHTML = "0"
            document.getElementById("b3").innerHTML = "0"
            document.getElementById("b4").innerHTML = "0"
            document.getElementById("b5").innerHTML = "0"
            attempts -= 1
            a.innerHTML = "Attempts Left: " + (attempts + 1)
        }
    }

    function gameover() {
        var b5 = document.getElementById("b5");
        var b4 = document.getElementById("b4");
        var b3 = document.getElementById("b3");
        var b2 = document.getElementById("b2");
        var b1 = document.getElementById("b1");
        var b0 = document.getElementById("b0");
        var sc = document.getElementById("sc");
        var directions = document.getElementById("directions");
        var tryAgain = document.getElementById("tryAgain");
        var check = document.getElementById("check")
        var para = document.getElementById("para")
        var num = document.getElementById("randomNumber")

        b5.style.display = "none"
        b4.style.display = "none"
        b3.style.display = "none"
        b2.style.display = "none"
        b1.style.display = "none"
        b0.style.display = "none"
        check.style.display = "none"
        para.style.display = "none"
        num.style.display = "none"
        directions.innerHTML = "YOU LOST! Press the button to try again."
        tryAgain.style.display = "inline-block"
    }

    function restart() {
        var score = 0;
        var attempts = 2;

        var buttons = document.getElementById("binary-buttons")
        var tryAgain = document.getElementById("tryAgain");
        var check = document.getElementById("check");
        var text = document.getElementById("directions");
        var number = document.getElementById("randomNumber")
        var b5 = document.getElementById("b5");
        var b4 = document.getElementById("b4");
        var b3 = document.getElementById("b3");
        var b2 = document.getElementById("b2");
        var b1 = document.getElementById("b1");
        var b0 = document.getElementById("b0");
        var sc = document.getElementById("sc");
        var a = document.getElementById("attempt")

        check.style.display = "inline-block";
        text.innerHTML = "Click the boxes to match the number in binary!";
        tryAgain.style.display = "none"; // Hide the "Start" button
        b5.style.display = "inline-block"
        b4.style.display = "inline-block"
        b3.style.display = "inline-block"
        b2.style.display = "inline-block"
        b1.style.display = "inline-block"
        b0.style.display = "inline-block"
        randNumber = randnum()
        number.style.display = "block"
        number.innerHTML = "Number: " + randNumber
        sc.innerHTML = "Score: " + score
        a.innerHTML = "Attemtps Left: " + (attempts + 1)
    }

        //When people open the website, the high scores function is called and will list the top 3 people with the highest scores.
        window.addEventListener('load', () => {
        highScores();})

    //Function takes the user data stores in the cookie for username
    function getCookie(name) {
        const cookies = document.cookie.split(";");
            for (const cookie of cookies) {
                const parts = cookie.split("=");
                const cookieName = parts[0].trim();
                if (cookieName === name) {
                    return decodeURIComponent(parts[1]);
                }
            }
        }

    //Function for posting user data into the backend
    function saveUserData(username, score) {
        //var apiUrl = 'https://asianunited.stu.nighthawkcodingsociety.com/api/players/guess/update';
        var apiUrl = 'http://127.0.0.1:8640/api/players/binary/update';

        var un = username;

        // Create an object with the data to send to the server
        var data = {
            username: un,
            gScore: score
        };

        // Send a POST request to update the user's data
        fetch(apiUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(data),
        })
        .then(response => {
            if (response.status === 200) {
                // Data saved successfully
                console.log('User data saved successfully');
            } else {
                // Handle errors
                console.error('Failed to save user data');
            }
        })
        .catch(error => {
            console.error('Error:', error);
        });
    }

    //Generates top 3 highest scorers
    function highScores() {
        // Fetch high scores from the Flask API
        $.ajax({
            //url: "https://asianunited.stu.nighthawkcodingsociety.com/api/players/highbinary",  // Use the appropriate URL for high scores
            url: "http://127.0.0.1:8640/api/players/highbinary",  // Use the appropriate URL for high scores
            method: "GET",
            dataType: "json",
            success: function(data) {
                // Iterate through the high scores and populate the table
                data.forEach(function(score) {
                    $("#highScoreBody").append(
                        "<tr>" +
                        "<td>" + score.position + "</td>" +
                        "<td>" + score.username + "</td>" +
                        "<td>" + score.highscore + "</td>" +
                        "</tr>"
                    );
                });
            },
            error: function() {
                console.log("Failed to fetch high scores.");
            }
        });
    };

    $(document).ready(function() {
        fetchAndDisplayHighScores();
    });

</script>
</html>

Backend Work: Model

import json
from __init__ import app, db
from sqlalchemy.exc import IntegrityError

# Define a class for CookieClicker table
class CookieClicker(db.Model):
    __tablename__ = 'cookieclicker'
    #Define Class Schema
    id = db.Column(db.Integer, primary_key=True)
    playerID = db.Column(db.Integer, db.ForeignKey('players.id'), nullable=False)
    cScore = db.Column(db.Integer, unique=False)
    cCost = db.Column(db.Integer, unique=False)
    cCount = db.Column(db.Integer, unique=False)
    dbCost = db.Column(db.Integer, unique=False)
    dbCount = db.Column(db.Integer, unique=False)
    pCount = db.Column(db.Integer, unique=False)
    rCost = db.Column(db.Integer, unique=False)
    rate = db.Column(db.Integer, unique=False)

    # Constructor to initialize the object
    def __init__(self, id, ccScore, cCost, cCount, dbCost, dbCount, pCount, rCost, rate):
        self.playerID = id
        self.cScore = ccScore
        self.cCost = cCost
        self.cCount = cCount
        self.dbCost = dbCost
        self.dbCount = dbCount
        self.pCount = pCount
        self.rCost = rCost
        self.rate = rate

    # String representation of the object
    def __repr__(self):
        return "CookieClicker(" + str(self.playerID) + "," + str(self.cScore) + "," + str(self.cCost) + "," + str(self.cCount) + "," + str(self.dbCost) + "," + str(self.dbCount) + "," + str(self.pCount) + "," + str(self.rCost) + "," + str(self.rate) + ")"
    
    # Method to create a record in the table
    def create(self):
        try:
            # creates a Notes object from Notes(db.Model) class, passes initializers
            db.session.add(self)  # add prepares to persist person object to Notes table
            db.session.commit()  # SqlAlchemy "unit of work pattern" requires a manual commit
            return self
        except IntegrityError:
            db.session.remove()
            return None
    
    # Method to read the object's data
    def read(self):
        return {
            "playerID": self.playerID,
            "cookieScore": self.cScore,
            "clickerCost": self.cCost,
            "clickerCount": self.cCount,
            "doubleCost": self.dbCost,
            "doubleCount": self.dbCount,
            "plusCount": self.pCount,
            "rateCost": self.rCost,
            "rate": self.rate
        }
    
    # Method to update the object's data
    def update(self, ccScore, cCost, cCount, dbCost, dbCount, pCount, rCost, rate):
        #Convvert values into integers
        ccScore = int(ccScore)
        cCost = int(cCost)
        cCount = int(cCount)
        dbCost = int(dbCost)
        dbCount = int(dbCount)
        pCount = int(pCount)
        rCost = int(rCost)
        rate = int(rate)

        #Check to see if values are higher than previous values
        if ccScore > self.cScore:
            self.cScore = ccScore
        if cCost > self.cCost:
            self.cCost = cCost
        if dbCost > self.dbCost:
            self.dbCost = dbCost
        if dbCount > self.dbCount:
            self.dbCount = dbCount
        if pCount > self.pCount:
            self.pCount = pCount
        if rCost > self.rCost:
            self.rCost = rCost
        if rate > self.rate:
            self.rate = rate
        self.cCount = cCount

class BinaryGame(db.Model):
    __tablename__ = 'binarygame'
    #Define Class Schema
    id = db.Column(db.Integer, primary_key=True)
    playerID = db.Column(db.Integer, db.ForeignKey('players.id'), nullable=False)
    bScore = db.Column(db.Integer, unique=False)

    def __init__(self, id, bScore):
        self.playerID = id
        self.bScore = bScore
    
    def __repr__(self):
        return "BinaryGame(" + str(self.playerID) + "," + str(self.bScore) + ")"
    
    def create(self):
        try:
            # creates a Notes object from Notes(db.Model) class, passes initializers
            db.session.add(self)  # add prepares to persist person object to Notes table
            db.session.commit()  # SqlAlchemy "unit of work pattern" requires a manual commit
            return self
        except IntegrityError:
            db.session.remove()
            return None
    
    def read(self):
        return {
            "playerID": self.playerID,
            "binaryScore": self.bScore
        }
    
    def update(self, bScore):
        #Convvert values into integers
        bScore = int(bScore)

        #Check to see if score is higher than previous score
        if bScore > self.bScore:
            self.bScore = bScore

class GuessGame(db.Model):
    __tablename__ = 'guessgame'
    #Define Class Schema
    id = db.Column(db.Integer, primary_key=True)
    playerID = db.Column(db.Integer, db.ForeignKey('players.id'), nullable=False)
    gScore = db.Column(db.Integer, unique=False)

    def __init__(self, id, gScore):
        self.playerID = id
        self.gScore = gScore
    
    def __repr__(self):
        return "GuessGame(" + str(self.playerID) + "," + str(self.gScore) + ")"
    
    def create(self):
        try:
            # creates a Notes object from Notes(db.Model) class, passes initializers
            db.session.add(self)  # add prepares to persist person object to Notes table
            db.session.commit()  # SqlAlchemy "unit of work pattern" requires a manual commit
            return self
        except IntegrityError:
            db.session.remove()
            return None
    
    def read(self):
        return {
            "playerID": self.playerID,
            "guessScore": self.gScore
        }
    
    def update(self, gScore):
        #Convvert values into integers
        gScore = int(gScore)

        #Check to see if score is higher than previous score
        if gScore > self.gScore:
            self.gScore = gScore

class Player(db.Model):
    __tablename__= 'players'

    id = db.Column(db.Integer, primary_key=True)

    _username = db.Column(db.Text, unique=True, nullable=False)

    #Relationships between the tables
    cookieclicker = db.relationship("CookieClicker", cascade='all, delete', backref='players', lazy=True)
    binarygame = db.relationship("BinaryGame", cascade='all, delete', backref='players', lazy=True)
    guessgame = db.relationship("GuessGame", cascade='all, delete', backref='players', lazy=True)

    def __init__(self, username, fullname):
        self.username = username

    @property
    def username(self):
        return self._username
    
    @username.setter
    def username(self, username):
        self._username = username
    
    def get_id(self):
        return self.id
    
    def __str__(self):
        return json.dumps(self.read())
    
    # CRUD create/add a new record to the table
    # returns self or None on error
    def create(self):
        try:
            # creates a person object from User(db.Model) class, passes initializers
            db.session.add(self)  # add prepares to persist person object to Users table
            db.session.commit()  # SqlAlchemy "unit of work pattern" requires a manual commit
            return self
        except IntegrityError:
            db.session.remove()
            return None
    
    #CRUD read: Read converts self to dictionary
    #Returns dictionary
    def read(self):
        return {
            "id": self.id,
            "username": self.username,
            "cookieScore": self.cScore,
            "binaryScore": self.bScore,
            "guessScore": self.gScore
        }
    
    # CRUD update: updates user name, password, phone
    # returns self
    def update(self, username=""):
        #only updates values with length
        if len(username) > 0:
            self.username = username
        db.session.commit()
        return self
    
    # CRUD delete: remove self
    # None
    def delete(self):
        db.session.delete(self)
        db.session.commit()
        return None
    
#Adding some users into the database
def initUsers():
    with app.app_context():
        """Create database and tables"""
        db.init_app(app)
        db.create_all()

        u1 = Player(username='Vegapondz')
        u2 = Player(username='Plate0')
        u3 = Player(username='Drown')
        u4 = Player(username='Jojajeto')
        
        # Inserting test data into CookieClicker table
        u1.cookieclicker.append(CookieClicker(id=u1.id, ccScore=100, cCost=100, cCount=100, dbCost=100, dbCount=100, pCount=100, rCost=100, rate=100))
        u2.cookieclicker.append(CookieClicker(id=u2.id, ccScore=200, cCost=200, cCount=200, dbCost=200, dbCount=200, pCount=200, rCost=200, rate=200))
        u3.cookieclicker.append(CookieClicker(id=u3.id, ccScore=300, cCost=300, cCount=300, dbCost=300, dbCount=300, pCount=300, rCost=300, rate=300))
        u4.cookieclicker.append(CookieClicker(id=u4.id, ccScore=400, cCost=400, cCount=400, dbCost=400, dbCount=400, pCount=400, rCost=400, rate=400))

        # Inserting test data into BinaryGame table
        u1.binarygame.append(BinaryGame(id=u1.id, bScore=10))
        u2.binarygame.append(BinaryGame(id=u2.id, bScore=15))
        u3.binarygame.append(BinaryGame(id=u3.id, bScore=20))
        u4.binarygame.append(BinaryGame(id=u4.id, bScore=25))

        # Inserting test data into GuessGame table
        u1.guessgame.append(GuessGame(id=u1.id, gScore=10))
        u2.guessgame.append(GuessGame(id=u2.id, gScore=10))
        u3.guessgame.append(GuessGame(id=u3.id, gScore=10))
        u4.guessgame.append(GuessGame(id=u4.id, gScore=10))

        #Attempt to create users
        users = [u1, u2, u3, u4]
        for user in users:
            try:
                user.create()
            except IntegrityError:
                '''fails with bad or duplicate data'''
                db.session.remove()
                print(f"Records exist, duplicate uid, or error: {user.uid}")

Backend Work: Player API

from flask import Blueprint, request, jsonify, abort
from flask_restful import Api, Resource # used for REST API building
from model.playerz import Player
from model.playerz import CookieClicker
from model.playerz import BinaryGame
from model.playerz import GuessGame

# Create a Flask Blueprint for the player API with a URL prefix
player_api = Blueprint('player_api', __name__,
                   url_prefix='/api/players')

# API docs https://flask-restful.readthedocs.io/en/latest/api.html
api = Api(player_api)

# Define helper functions to find objects in the database by username
def cookieclicker_obj_by_username(username):
    """finds User in table matching username """
    id = Player.query.filter_by(_username=username).first().id
    return CookieClicker.query.filter_by(id=id).first()

def binarygame_obj_by_username(username):
    """finds User in table matching username """
    id = Player.query.filter_by(_username=username).first().id
    return BinaryGame.query.filter_by(id=id).first()

def guessgame_obj_by_username(username):
    """finds User in table matching username """
    id = Player.query.filter_by(_username=username).first().id
    return GuessGame.query.filter_by(id=id).first()

def findId(username): 
    id = Player.query.filter_by(_username=username).first().id
    return id 

def findPlayer(username): 
    player = Player.query.filter_by(_username=username).first()
    return player

# Create a class for the Player API
class PlayerAPI:
    # Nested class for creating a new player
    class _Create(Resource):
        def post(self):
            ''' Read data for json body '''
            body = request.get_json()
            
            ''' Avoid garbage in, error checking '''
            # validate name
            username = body.get('username')
            if username is None or len(username) < 2:
                return {'message': f'Username is missing, or is less than 2 characters'}, 400
            
            po = Player(username=username)

            player = po.create()
            #Sucess returns json of user
            if player:
                return jsonify(player.read())
            #Failure returns error
            return {'message': f'Processed {username}, either a format error or duplicate'}, 400
    
    # Nested class for authenticating a player
    class _Authenticate(Resource):
        def post(self):
            body = request.get_json()
            username = body.get('username')
            password = body.get('password')

            # Authenticate the user, e.g., by checking their credentials in the database
            user = findPlayer(username)
            
            if user and user.check_password(password):
                return {'message': 'Authentication successful'}
            else:
                return {'message': 'Authentication failed'}, 401  # Use proper HTTP status code for unauthorized

    class _Read(Resource):
        def get(self):
            players = Player.query.all()    # read/extract all users from database
            json_ready = [player.read() for player in players]  # prepare output in json
            return jsonify(json_ready)  # jsonify creates Flask response object, more specific to APIs than json.dumps
    
    class _ScoreCookie(Resource):
        def get(self):
            players = Player.query.all()
            cscore = [Player.cScore for player in players]
            return jsonify(cscore)
    
    class _HighScoreCookie(Resource):
        def get(self):
            players = Player.query.all()
            
            # Sort players by cScore in descending order
            sorted_players = sorted(players, key=lambda player: player.cScore, reverse=True)
            
            high_scores = []
            for i, player in enumerate(sorted_players[:3]):  # Get the top 3 high scorers
                if player.cScore is not None:
                    high_scores.append({
                        'position': i + 1,
                        'username': player.username,
                        'highscore': player.cScore
                    })
            
            return jsonify(high_scores)
    
    class _DeleteCookie(Resource):               
        def delete(self):
            body = request.get_json()               # We grab our body
            username = body.get('username')         # Get the username of the user from the cookie, will process in frontend
            player = cookieclicker_obj_by_username(username)
            if player:                                # Check if user exists
                player.delete()                       # call delete
            else:                                   # if user does not exist
                return {'message': f"unable to find Cookie Clicker entries of user '{username}'"}, 404
            return player.read()
    
    class _UpdateCookie(Resource):
        def post(self):
            body = request.get_json()
            username = body.get('username')
            ccScore = int(body.get('cScore'))
            cCost = int(body.get('cCost'))
            cCount = int(body.get('cCount'))
            dbCost = int(body.get('dbCost'))
            dbCount = int(body.get('dbCount'))
            pCount = int(body.get('pCount'))
            rCost = int(body.get('rCost'))
            rate = int(body.get('rate'))

            player = cookieclicker_obj_by_username(username)
            if player:
                player.update(ccScore, cCost, cCount, dbCost, dbCount, pCount, rCost, rate)
            else:
                return {'message': f"unable to find Cookie Clicker entries of user '{username}'"}, 404     # error msg
            return player.read()
    
    class _ScoreBinary(Resource):
        def get(self):
            players = Player.query.all()
            cscore = [Player.bScore for player in players]
            return jsonify(cscore)
    
    class _HighScoreBinary(Resource):
        def get(self):
            players = Player.query.all()
            
            # Sort players by cScore in descending order
            sorted_players = sorted(players, key=lambda player: player.cbcore, reverse=True)
            
            high_scores = []
            for i, player in enumerate(sorted_players[:3]):  # Get the top 3 high scorers
                if player.bScore is not None:
                    high_scores.append({
                        'position': i + 1,
                        'username': player.username,
                        'highscore': player.bScore
                    })
            
            return jsonify(high_scores)
    
    class _DeleteBinary(Resource):                     # This resource aims to delete a gpa row in the db
        def delete(self):
            body = request.get_json()               # We grab our body
            username = body.get('username')         # Get the username of the user from the cookie, will process in frontend
            player = binarygame_obj_by_username(username)
            if player:                                # Check if user exists
                player.delete()                       # call delete
            else:                                   # if user does not exist
                return {'message': f"unable to find Binary Game entries of user '{username}'"}, 404
            return player.read()
    
    class _UpdateBinary(Resource):
        def post(self):
            body = request.get_json()
            username = body.get('username')
            bScore = int(body.get('bScore'))

            player = binarygame_obj_by_username(username)
            if player:
                player.update(bScore)
            else:
                return {'message': f"unable to find Binary Game entries of user '{username}'"}, 404    # error msg
            return player.read()
    
    class _ScoreGuess(Resource):
        def get(self):
            players = Player.query.all()
            cscore = [Player.gScore for player in players]
            return jsonify(cscore)
    
    class _HighScoreGuess(Resource):
        def get(self):
            players = Player.query.all()
            
            # Sort players by cScore in descending order
            sorted_players = sorted(players, key=lambda player: player.gbcore, reverse=True)
            
            high_scores = []
            for i, player in enumerate(sorted_players[:3]):  # Get the top 3 high scorers
                if player.gScore is not None:
                    high_scores.append({
                        'position': i + 1,
                        'username': player.username,
                        'highscore': player.gScore
                    })
            
            return jsonify(high_scores)
    
    class _DeleteGuess(Resource):                     
        def delete(self):
            body = request.get_json()               # We grab our body
            username = body.get('username')         # Get the username of the user from the cookie, will process in frontend
            player = guessgame_obj_by_username(username)
            if player:                                # Check if user exists
                player.delete()                       # call delete
            else:                                   # if user does not exist
                return {'message': f"unable to find Guess Game entries of user '{username}'"}, 404
            return player.read()
    
    class _UpdateGuess(Resource):
        def post(self):
            body = request.get_json()
            username = body.get('username')
            gScore = int(body.get('gScore'))

            player = guessgame_obj_by_username(username)
            if player:
                player.update(gScore)
            else:
                return {'message': f"unable to find Guess Game entries of user '{username}'"}, 404     # error msg
            return player.read()
        
    api.add_resource(_Create, '/create')
    api.add_resource(_Authenticate, '/auth')
    api.add_resource(_Read, '/')
    api.add_resource(_ScoreCookie, '/cookie')
    api.add_resource(_HighScoreCookie, '/highcookie')
    api.add_resource(_DeleteCookie, '/cookie/delete')
    api.add_resource(_UpdateCookie, '/cookie/update')
    api.add_resource(_ScoreBinary, '/binary')
    api.add_resource(_HighScoreBinary, '/highbinary')
    api.add_resource(_DeleteBinary, '/binary/delete')
    api.add_resource(_UpdateBinary, '/binary/update')
    api.add_resource(_ScoreGuess, '/guess')
    api.add_resource(_HighScoreGuess, '/highguess')
    api.add_resource(_DeleteGuess, '/guess/delete')
    api.add_resource(_UpdateGuess, '/guess/update')


Reflection on the Project


Week Reflection
Week 1 We have a lot of ideas for our project, but we didn't really know where to go with it. At first, we wanted to build an aim trainer game but we thought it would be way too complicated for our project. We were also stuck inbetween multiple games that we wanted to do, so instead we just created one huge website that would hold multiple games. Mr. Lopez gave us an idea to have the games held in the backend, so we are trying to figure out how to fetch the games to the frontend.
Week 2 Matthew started to code the cookie clicker game that we wanted to turn into a gacha game. He got decent progress on it, actually. We also struggled to figure out the frontend and backend repositories for a while, and we didn't fully deploy the website yet. Progress this week was quite slow, and I only got a small bit of the binary game done.
Week 3 This week, I figured out how to use JavaScript and HTML to get the binary game to function properly. The game looks very rough, though. If I was given more time, I would probably use CSS to make the game look a little better than it does right now, but CSS and HTML don't get graded so I suppose it wouldn't be vry smart to use my time to make it look better. Right now, we are struggling to link the frontend to the backend. It makes me a little worried.
Week 4 This week, we finally got some of the backend working. We were able to connect the frontend to the backend. Matthew and I both made APIs to keep score for our games. This is also the week of N@tM, and we were able to qualify after one day of rest on Wednesday. Matthew did some research on databases and we still haven't gotten that working exactly, but hopefully by this weekend it will be operating.