diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2eabec --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.venv +.env \ No newline at end of file diff --git a/db.sqlite b/db.sqlite new file mode 100644 index 0000000..a16919c Binary files /dev/null and b/db.sqlite differ diff --git a/flask_session/2029240f6d1128be89ddc32729463129 b/flask_session/2029240f6d1128be89ddc32729463129 new file mode 100644 index 0000000..60b84f8 Binary files /dev/null and b/flask_session/2029240f6d1128be89ddc32729463129 differ diff --git a/index.html b/index.html deleted file mode 100644 index ee49c02..0000000 --- a/index.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - Alfie's basement - - - - - - - - - - - - - - -
- -
-
-
- -
-

Alfie King

-

server backend survivor

-
-
-
- -
-

A lil bit abt me

-

- Im not good with writing so dont expect much here. I am a student who is learning c++ and python. I've Done a few projects that i think - are decent enough to show off, so I have put them on this website. I like to mess around with linux and have a few servers that I run. I've - been running a server for a few years now, and I have learned a lot from it. I have also switched to linux on my main computer, which has been - slightly annoying at times (mainly because one of my most played games' anticheat doesn't support on linux atm. Also, the lack of photoshop is - a pain). -

- I would like to make some more projects in the future, but I am not sure what I want to make yet. I tend to make thing on impulse a lot, and motivation - is "lacking" at times. So the few ideas I do have may never come to fruition. I hope to get better at art so i could hopefully make a game that is somewhat - interesting. But im at a lack of ideas at the moment. -

- I would also like to have a functional blog on this site, but I bearly talk about much so I dont know what I would write about. I like to ramble on about - random things, but I dont think that would be very interesting to read, and I think that I would forget to update it. I have a tumblr that I have had for a few - years now, but I dont post on it (the social anxiety is too much for me :<). However I hope to get better at that in the future. -

-
-
- - - - - - - - -
-
- -
-

-

-
-
-
- - - - - - - - - - - - - - - - -
-
-
-

Projects & stuff

-

just some projects ive worked on over time

- -
-
-

The button collection™

-
-
-
-
-

Some News

-
(dont expect this to be updated often tho :P)
- -
-
- -
-
- - - diff --git a/src/__pycache__/database.cpython-313.pyc b/src/__pycache__/database.cpython-313.pyc new file mode 100644 index 0000000..1a92a2f Binary files /dev/null and b/src/__pycache__/database.cpython-313.pyc differ diff --git a/src/database.py b/src/database.py new file mode 100644 index 0000000..0a0eef5 --- /dev/null +++ b/src/database.py @@ -0,0 +1,40 @@ +import sqlite3 + +class Database: + def __init__(self, db_name='db.sqlite'): + self.connection = sqlite3.connect(db_name, check_same_thread=False) + self.cursor = self.connection.cursor() + self.create_snake_table() + + def create_snake_table(self): + self.cursor.execute(''' + CREATE TABLE IF NOT EXISTS snake ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + score INTEGER NOT NULL + ) + ''') + self.connection.commit() + + def insert_snake(self, name, score): + old_score = self.get_snake_score(name) + if old_score is not None and score <= old_score: + return + + self.cursor.execute(''' + INSERT INTO snake (name, score) + VALUES (?, ?) + ''', (name, score)) + self.connection.commit() + + def get_snake_score(self, name): + self.cursor.execute('SELECT score FROM snake WHERE name = ?', (name,)) + result = self.cursor.fetchone() + return result[0] if result else None + + def get_snake_scores(self): + self.cursor.execute('SELECT * FROM snake ORDER BY score DESC') + return self.cursor.fetchall() + + def close(self): + self.connection.close() \ No newline at end of file diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..09ed11e --- /dev/null +++ b/src/main.py @@ -0,0 +1,80 @@ +from flask import Flask, request, render_template +from flask_session import Session +from dotenv import load_dotenv +from os import getenv as env +import logging, database, requests + + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +load_dotenv() + + +app = Flask( + __name__, + template_folder=env('TEMPLATE_FOLDER', default='templates'), + static_folder=env('STATIC_FOLDER', default='static'), +) +app.config["SESSION_PERMANENT"] = True +app.config["SESSION_TYPE"] = "filesystem" +Session(app) + + +db = database.Database(db_name=env('DB_NAME', default='db.sqlite')) + + +@app.route('/') +def index(): + logging.info("Rendering index page") + return render_template('index.html') + + +@app.route('/snake/submit', methods=['POST']) +def snake_submit(): + data = request.json + if not data or 'name' not in data or 'score' not in data: + logging.error("Invalid data received: %s", data) + return {'error': 'Invalid data'}, 400 + + name = data.get('name', '') + cap = data.get('cap', '') + score = data.get('score', -1) + + cap_response = requests.post( + env('CAP_VERIFY_URL', default='https:////siteverify'), + json={ + 'secret': env('CAP_SECRET', default=''), + 'response': cap, + } + ) + + if cap_response.status_code != 200 or not cap_response.json().get('success', "false") != "true": + logging.error("Captcha verification failed: %s", cap_response.json()) + return {'error': 'Captcha verification failed'}, 400 + + if not isinstance(name, str) or not isinstance(score, int): + logging.error("Invalid data types: name=%s, score=%s", type(name), type(score)) + return {'error': 'Invalid data types'}, 400 + + if not name or score <= 0 or score > 10000: + logging.error("Invalid name or score: name=%s, score=%s", name, score) + return {'error': 'Invalid name or score'}, 400 + + db.insert_snake(name, score) + logging.info("Snake submitted: name=%s, score=%d", name, score) + return {'success': True, 'message': 'Snake submitted successfully'}, 200 + + +@app.errorhandler(404) +def page_not_found(e): + logging.error("Page not found: %s", request.path) + unformatted_scores = db.get_snake_scores() + scores = [{'position': i + 1, 'name': score[1], 'score': score[2]} for i, score in enumerate(unformatted_scores)] + return render_template('404.html', scores=scores), 404 + + +if __name__ == '__main__': + app.run( + host=env('HOST', default='0.0.0.0'), + port=env('PORT', default=5000), + debug=env('DEBUG', default=False).lower() == 'true' + ) \ No newline at end of file diff --git a/static/content/Irken-Like-AllCaps.woff b/src/static/content/Irken-Like-AllCaps.woff similarity index 100% rename from static/content/Irken-Like-AllCaps.woff rename to src/static/content/Irken-Like-AllCaps.woff diff --git a/static/content/background.png b/src/static/content/background.png similarity index 100% rename from static/content/background.png rename to src/static/content/background.png diff --git a/static/content/buttons.txt b/src/static/content/buttons.txt similarity index 100% rename from static/content/buttons.txt rename to src/static/content/buttons.txt diff --git a/static/content/haj.gif b/src/static/content/haj.gif similarity index 100% rename from static/content/haj.gif rename to src/static/content/haj.gif diff --git a/static/content/icon.webp b/src/static/content/icon.webp similarity index 100% rename from static/content/icon.webp rename to src/static/content/icon.webp diff --git a/src/static/css/404.css b/src/static/css/404.css new file mode 100644 index 0000000..d3e572f --- /dev/null +++ b/src/static/css/404.css @@ -0,0 +1,79 @@ +canvas#snakeCanvas { + padding: 15px; + width: 100%; + box-sizing: border-box; +} + +form { + display: flex; + flex-direction: column; + width: min-content; + gap: 1rem; + padding: 15px; +} + +form input[type="text"] { + padding: 10px; + box-sizing: border-box; + border: 1px solid var(--secondary-background-color); + border-radius: 6px; + background-color: var(--secondary-background-color-but-slightly-transparent); + color: var(--text-color); +} + +form button[type="submit"] { + padding: 10px; + box-sizing: border-box; + border: 1px solid var(--secondary-background-color); + border-radius: 6px; + background-color: var(--secondary-background-color-but-slightly-transparent); + color: var(--text-color); + font-weight: bold; + cursor: pointer; +} + +.flex-row { + display: flex; + flex-direction: row; + gap: 1rem; +} + +.min-width { + width: min-content; +} + +.max-width { + width: 100%; +} + +#snakeLeaderboardSection { + display: flex; + flex-direction: column; + align-items: center; + padding: 0; + max-height: 272px ; +} + +#snakeLeaderboard { + display: flex; + flex-direction: column; + list-style: none; + padding: 0; + margin: 0; + width: 100%; + overflow-y: scroll; +} + +#snakeLeaderboard li { + padding: 5px 20px; + background-color: var(--secondary-background-color-but-slightly-transparent); + color: var(--text-color); + font-size: 1rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +#snakeLeaderboard li:nth-child(even) { + background-color: var(--secondary-background-color); +} \ No newline at end of file diff --git a/src/static/css/cap.css b/src/static/css/cap.css new file mode 100644 index 0000000..d978715 --- /dev/null +++ b/src/static/css/cap.css @@ -0,0 +1,21 @@ +cap-widget { + --cap-background: var(--secondary-background-color-but-slightly-transparent); + --cap-border-color: var(--secondary-background-color); + --cap-border-radius: 14px; + --cap-widget-height: 30px; + --cap-widget-width: 230px; + --cap-widget-padding: 14px; + --cap-gap: 15px; + --cap-color: var(--text-color); + --cap-checkbox-size: 25px; + --cap-checkbox-border: 1px solid var(--secondary-background-color); + --cap-checkbox-border-radius: 6px; + --cap-checkbox-background: none; + --cap-checkbox-margin: 2px; + --cap-font: "Space Mono", "serif"; + --cap-spinner-color: var(--primary-color); + --cap-spinner-background-color: var(--secondary-background-color-but-slightly-transparent); + --cap-spinner-thickness: 5px; + --cap-credits-font-size: 12px; + --cap-opacity-hover: 0.8; +} \ No newline at end of file diff --git a/static/css/index.css b/src/static/css/index.css similarity index 100% rename from static/css/index.css rename to src/static/css/index.css diff --git a/static/js/index.js b/src/static/js/index.js similarity index 99% rename from static/js/index.js rename to src/static/js/index.js index b0f6ce8..1c983a7 100644 --- a/static/js/index.js +++ b/src/static/js/index.js @@ -1,5 +1,3 @@ -// TYPERWRITER - const values = [ "Web developer", "Pc games enjoyer", diff --git a/src/static/js/snake.js b/src/static/js/snake.js new file mode 100644 index 0000000..146a8e0 --- /dev/null +++ b/src/static/js/snake.js @@ -0,0 +1,151 @@ +const canvas = document.getElementById('snakeCanvas'); +const ctx = canvas.getContext('2d'); + +const gridSize = 15; +const scale = 200; +canvas.width = gridSize * scale; +canvas.height = gridSize * scale; + +let snake = [{ x: 10, y: 10 }]; +let direction = { x: 0, y: 0 }; +let food = { x: 5, y: 5 }; +let score = 0; +let gameOver = false; + +let token = null; + +function draw() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // draw grid of checkerboard pattern + for (let x = 0; x < gridSize; x++) { + for (let y = 0; y < gridSize; y++) { + ctx.fillStyle = (x + y) % 2 === 0 ? 'darkgray' : 'grey'; + ctx.fillRect(x * scale, y * scale, scale, scale); + } + } + + // Draw snake + snake.forEach(segment => { + ctx.fillStyle = (snake.indexOf(segment) % 2 === 0) ? 'green' : 'darkgreen'; + ctx.fillRect(segment.x * scale, segment.y * scale, scale, scale); + }); + + // Draw food + ctx.fillStyle = 'red'; + ctx.fillRect(food.x * scale, food.y * scale, scale, scale); +} + +function update() { + if (gameOver) return; + + // Move snake + const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y }; + + // Add new head + snake.unshift(head); + + // Check for food collision + if (head.x === food.x && head.y === food.y) { + score += 10; // Increase score + placeFood(); + } else { + snake.pop(); // Remove tail if no food eaten + } + + // Check for wall collision + if (head.x < 0 || head.x >= gridSize || head.y < 0 || head.y >= gridSize) { + gameOver = true; + alert(`Game Over! Your score: ${score}`); + return; + } + + // Check for self collision + for (let i = 1; i < snake.length; i++) { + if (head.x === snake[i].x && head.y === snake[i].y) { + gameOver = true; + alert(`Game Over! Your score: ${score}`); + return; + } + } +} + +function placeFood() { + do { + food.x = Math.floor(Math.random() * gridSize); + food.y = Math.floor(Math.random() * gridSize); + } while (snake.some(segment => segment.x === food.x && segment.y === food.y)); +} + +function changeDirection(event) { + switch (event.key) { + case 'w': + if (direction.y === 0) direction = { x: 0, y: -1 }; + break; + case 's': + if (direction.y === 0) direction = { x: 0, y: 1 }; + break; + case 'a': + if (direction.x === 0) direction = { x: -1, y: 0 }; + break; + case 'd': + if (direction.x === 0) direction = { x: 1, y: 0 }; + break; + } +} + +function gameLoop() { + if (!gameOver) { + update(); + draw(); + setTimeout(gameLoop, 100); + } +} + +document.addEventListener('keydown', changeDirection); +// Start the game loop +gameLoop(); +// Initial draw +draw(); + + +const widget = document.querySelector("#cap"); + +widget.addEventListener("solve", function (e) { + token = e.detail.token; + console.log("Captcha solved, token:", token); +}); + +// Function to submit score to the server +document.getElementById("snakeForm").addEventListener('submit', function(event) { + event.preventDefault(); + + if (!token) { + alert('Please complete the CAPTCHA before submitting your score.'); + return; + } + + // post request to server with score + fetch('/snake/submit', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + score: score, + name: document.getElementById('name').value, + cap: token + }) + }).then(response => { + if (response.ok) { + alert('Score submitted successfully!'); + window.location.reload(); + } else { + alert('Failed to submit score, check console for details.'); + response.json().then(data => console.error(data)); + } + }).catch(error => { + console.error('Error submitting score:', error); + alert('Error submitting score, check console for details.'); + }); +}); \ No newline at end of file diff --git a/src/templates/404.html b/src/templates/404.html new file mode 100644 index 0000000..add651c --- /dev/null +++ b/src/templates/404.html @@ -0,0 +1,48 @@ +{% extends "base.html" %} + +{% block title %}404 - Not Found{% endblock %} +{% block description %}The page you are looking for does not exist.{% endblock %} + +{% block head %} + + +{% endblock %} + +{% block content %} +
+

404

+

+ It seems like the thing you are looking for is not here :[ +

+ while you're here, why not play some snake? (use wasd to move, not mobile compatable :<) +

+ +
+
+
+

Submit score

+
+ + + +
+
+
+

Leaderboard

+
    + {% for score in scores %} +
  • + {{ score.position }} + {{ score.name }} + {{ score.score }} +
  • + {% endfor %} +
+
+
+{% endblock %} + +{% block scripts %} + + +{% endblock %} \ No newline at end of file diff --git a/404.html b/src/templates/base.html similarity index 76% rename from 404.html rename to src/templates/base.html index 4b902f4..956d598 100644 --- a/404.html +++ b/src/templates/base.html @@ -3,19 +3,21 @@ - Alfie's basement + {% block title %}Alfie's basement{% endblock %} - + - - + + + {% block head %} + {% endblock %}
@@ -24,14 +26,14 @@

Things to see :3

@@ -70,18 +72,26 @@ -
-

404

-

- It seems like the thing you are looking for is not here :[ -

-
+ + {% block content %}{% endblock %}

legal stuff idk :3 | icba to © this :P | made with ♥ and caffine

+ {% block scripts %}{% endblock %} diff --git a/src/templates/index.html b/src/templates/index.html new file mode 100644 index 0000000..6f33ca9 --- /dev/null +++ b/src/templates/index.html @@ -0,0 +1,115 @@ +{% extends "base.html" %} + +{% block title %}Alfie's basement{% endblock %} +{% block description %}server backend survivor{% endblock %} + +{%block content %} +
+

A lil bit abt me

+

+ Im not good with writing so dont expect much here. I am a student who is learning c++ and python. I've Done a few projects that i think + are decent enough to show off, so I have put them on this website. I like to mess around with linux and have a few servers that I run. I've + been running a server for a few years now, and I have learned a lot from it. I have also switched to linux on my main computer, which has been + slightly annoying at times (mainly because one of my most played games' anticheat doesn't support on linux atm. Also, the lack of photoshop is + a pain). +

+ I would like to make some more projects in the future, but I am not sure what I want to make yet. I tend to make thing on impulse a lot, and motivation + is "lacking" at times. So the few ideas I do have may never come to fruition. I hope to get better at art so i could hopefully make a game that is somewhat + interesting. But im at a lack of ideas at the moment. +

+ I would also like to have a functional blog on this site, but I bearly talk about much so I dont know what I would write about. I like to ramble on about + random things, but I dont think that would be very interesting to read, and I think that I would forget to update it. I have a tumblr that I have had for a few + years now, but I dont post on it (the social anxiety is too much for me :<). However I hope to get better at that in the future. +

+
+
+ + + + + + + + +
+
+ +
+

+

+
+
+
+ + + + + + + + + + + + + + + + +
+
+
+

Projects & stuff

+

just some projects ive worked on over time

+
    +
  • +

    alfieking.dev

    +

    + This website is a project that I have been working on for a while now. I have made a few versions of it, but I have + never been happy with them. I am quite happy with this version atm since it is more organized and has a design that I + like. + source code +

    +
  • +
  • +

    owo (the terminal command)

    +

    + I made this project as a joke, I can't remember exactly what I baised it off other than the fact that it was somthing + similar to this but in a different language. I originally made it in python, but I have since rewritten it in c++ so + that it would be faster and so that I could learn c++. + source code +

    +
  • +
  • +

    prismic

    +

    + Prismic is a basic message board that I made, it was mainly made to learn how to use templating and more backend + web development. it uses flask for the web framework and uses a sqlite database to store the messages. I have thought + about remaking it in c++ since I found a c++ web framework that I would like to try out. + Primic | source code +

    +
  • +
+
+
+

The button collection™

+
+
+
+
+

Some News

+
(dont expect this to be updated often tho :P)
+
    +
  • +

    18-06-2025

    +

    + I plan on updating the site soon, I have a few ideas that I want to implement. I want to make it more + interactive and fun to use. I also want to add a blog section so that I can write about random things that I find interesting. I also + want to add a few more projects that I have been working on. Annoyingly I think it would be a good idea to remake this site with some sort of + framework so i can use templating, however this kinda bothers me since I like the simplicity of this site. And prefer to keep it as a static site + that i can just throw at nginx. +

    +
  • +
+
+{% endblock %} \ No newline at end of file diff --git a/robots.txt b/src/templates/robots.txt similarity index 100% rename from robots.txt rename to src/templates/robots.txt diff --git a/sitemap.xml b/src/templates/sitemap.xml similarity index 100% rename from sitemap.xml rename to src/templates/sitemap.xml