Compare commits
8 Commits
bfe6c47d8e
...
83b570214a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83b570214a | ||
|
|
460255db9e | ||
|
|
56a01ac5a7 | ||
|
|
4f6a45cbc7 | ||
|
|
4a998bca84 | ||
|
|
cc9cf0f1c2 | ||
|
|
3d7508fc29 | ||
|
|
d2ed2b2842 |
62
app.py
62
app.py
@@ -2,7 +2,7 @@ import asyncio
|
||||
import json
|
||||
import uuid
|
||||
from collections.abc import Iterator
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from enum import Enum
|
||||
from itertools import product
|
||||
from pathlib import Path
|
||||
@@ -166,13 +166,13 @@ class Room:
|
||||
self.turn = Color.WHITE
|
||||
self.board = Board()
|
||||
self.players = []
|
||||
self.last_move = datetime.now()
|
||||
self.last_move = datetime.now(timezone.utc)
|
||||
self.game_start = None
|
||||
self.state = State.NOT_FINISHED
|
||||
|
||||
def start(self):
|
||||
self.last_move = datetime.now()
|
||||
self.game_start = datetime.now()
|
||||
self.last_move = datetime.now(timezone.utc)
|
||||
self.game_start = datetime.now(timezone.utc)
|
||||
|
||||
def add_player(self) -> None | tuple[str, Color]:
|
||||
np: int = len(self.players)
|
||||
@@ -192,6 +192,20 @@ class Room:
|
||||
rooms: dict[str, Room] = {}
|
||||
|
||||
|
||||
favicon = Path("favicon.ico").read_bytes()
|
||||
favicon_update = Path("favicon_update.ico").read_bytes()
|
||||
|
||||
|
||||
@app.GET("/favicon.ico")
|
||||
async def favicon_s(request):
|
||||
return HTTPResponse(request, favicon, content_type="image/x-icon")
|
||||
|
||||
|
||||
@app.GET("/favicon_update.ico")
|
||||
async def favicon_update_s(request):
|
||||
return HTTPResponse(request, favicon_update, content_type="image/x-icon")
|
||||
|
||||
|
||||
@app.GET("/")
|
||||
async def home(request):
|
||||
return render(request, "index.html")
|
||||
@@ -218,12 +232,25 @@ async def join(request, room_id):
|
||||
"turn": room.turn.value,
|
||||
"board": room.board.serialize(),
|
||||
"ready": len(room.players) == 2,
|
||||
"state": room.state.value,
|
||||
"start_time": (
|
||||
room.game_start or datetime.now(timezone.utc)
|
||||
).isoformat(),
|
||||
},
|
||||
)
|
||||
else:
|
||||
return JSONResponse(
|
||||
request,
|
||||
{"code": "FULL", "error": "Room Full", "board": room.board.serialize()},
|
||||
{
|
||||
"code": "FULL",
|
||||
"error": "Room Full",
|
||||
"board": room.board.serialize(),
|
||||
"turn": room.turn.value,
|
||||
"state": room.state.value,
|
||||
"start_time": (
|
||||
room.game_start or datetime.now(timezone.utc)
|
||||
).isoformat(),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -392,6 +419,8 @@ async def move(request: Request, room_id):
|
||||
room = rooms[room_id]
|
||||
if len(room.players) != 2:
|
||||
return 400, {"code": "PRES", "error": "Game has not been started"}
|
||||
if room.state != State.NOT_FINISHED:
|
||||
return 400, {"code": "FINI", "error": "Game has not finished."}
|
||||
board = room.board
|
||||
uid = req["uid"]
|
||||
src = req["from"].lower()
|
||||
@@ -429,11 +458,27 @@ async def move(request: Request, room_id):
|
||||
if c.y == 0 or c.y == 7 and piece_kind == "p":
|
||||
board.grid[c.y][c.x] = Piece.WHITE_QUEEN if is_white else Piece.BLACK_QUEEN
|
||||
room.turn = Color.BLACK if color == Color.WHITE else Color.WHITE
|
||||
room.last_move = datetime.now()
|
||||
room.last_move = datetime.now(timezone.utc)
|
||||
opp_checkmate = True
|
||||
for i in range(8):
|
||||
for j in range(8):
|
||||
P = board.index_xy(i, j)
|
||||
if P != "E" and P.value.isupper() != is_white:
|
||||
moves = generate_valid_moves(
|
||||
P.value.lower(), board, not is_white, xy_to_pos_safe(i, j)
|
||||
)
|
||||
if len(moves) > 0:
|
||||
opp_checkmate = False
|
||||
break
|
||||
if not opp_checkmate:
|
||||
break
|
||||
if opp_checkmate:
|
||||
room.state = State.BLACK_WIN if is_white else State.WHITE_WIN
|
||||
return {
|
||||
"code": "MOVD",
|
||||
"color": color.value,
|
||||
"turn": room.turn.value,
|
||||
"state": room.state.value,
|
||||
"board": board.serialize(),
|
||||
}
|
||||
else:
|
||||
@@ -445,11 +490,11 @@ async def move(request: Request, room_id):
|
||||
async def poll(request, room_id):
|
||||
for key in rooms:
|
||||
room = rooms[key]
|
||||
if (datetime.now() - room.last_move) >= timedelta(hours=24):
|
||||
if (datetime.now(timezone.utc) - room.last_move) >= timedelta(hours=24):
|
||||
del rooms[key]
|
||||
if (
|
||||
room.game_start
|
||||
and (datetime.now() - room.game_start) >= timedelta(minutes=30)
|
||||
and (datetime.now(timezone.utc) - room.game_start) >= timedelta(minutes=30)
|
||||
and room.state == State.NOT_FINISHED
|
||||
):
|
||||
room.state = State.TIE
|
||||
@@ -462,6 +507,7 @@ async def poll(request, room_id):
|
||||
"board": room.board.serialize(),
|
||||
"ready": len(room.players) == 2,
|
||||
"state": room.state.value,
|
||||
"start_time": (room.game_start or datetime.now(timezone.utc)).isoformat(),
|
||||
}
|
||||
|
||||
|
||||
|
||||
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
favicon_update.ico
Normal file
BIN
favicon_update.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
icon_default.png
Normal file
BIN
icon_default.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
icon_update.png
Normal file
BIN
icon_update.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
125
index.html
125
index.html
@@ -28,8 +28,32 @@
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 24px 0;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#stats {
|
||||
font-family: 'Times New Roman', Times, serif;
|
||||
font-weight: 100;
|
||||
font-size: 32px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#clock {
|
||||
font-family: 'Times New Roman', Times, serif;
|
||||
font-weight: 100;
|
||||
font-size: 32px;
|
||||
color: #000;
|
||||
letter-spacing: 6px;
|
||||
}
|
||||
|
||||
#turn {
|
||||
border-top: 1px solid #68687aaa;
|
||||
padding-top: 4px;
|
||||
font-size: 14px;
|
||||
color: #68687aaa;
|
||||
}
|
||||
|
||||
.join-menu {
|
||||
@@ -80,6 +104,7 @@
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -89,6 +114,9 @@
|
||||
<div class="center">
|
||||
<div id="join-menu" class="join-menu"><input type="text" id="room-id" placeholder="Room ID"><button
|
||||
id="join-submit">Join</button></div>
|
||||
<h2 id="stats"></h2>
|
||||
<h2 id="clock"></h2>
|
||||
<h4 id="turn"></h4>
|
||||
</div>
|
||||
<div class="center">
|
||||
<canvas class="game" id="canvas"></canvas>
|
||||
@@ -112,11 +140,17 @@
|
||||
let mHouse = [0, 0];
|
||||
const textures = {};
|
||||
|
||||
const piecemoveSound = new Audio("/static/piecemove.mp3");
|
||||
//const endgameSound = new Audio("/static/endgame.mp3");
|
||||
|
||||
let color = 0;
|
||||
let turn = 0;
|
||||
let state = -1;
|
||||
let start_time = undefined;
|
||||
let ready = false;
|
||||
let letterMap = ' ';
|
||||
let numberMap = ' ';
|
||||
let oldBoard = undefined;
|
||||
let board = undefined;
|
||||
let UID = undefined;
|
||||
let ROOM_ID = undefined;
|
||||
@@ -124,6 +158,82 @@
|
||||
let selected = undefined;
|
||||
let moves = [];
|
||||
|
||||
function handleVisibilityChange() {
|
||||
if (document.visibilityState === 'visible') {
|
||||
let link = document.querySelector("link[rel~='icon']");
|
||||
|
||||
link.href = "/favicon.ico";
|
||||
link.type = "image/x-icon";
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
function boardOnChange() {
|
||||
if (JSON.stringify(board) !== JSON.stringify(oldBoard)) {
|
||||
if (document.visibilityState !== 'visible') {
|
||||
let link = document.querySelector("link[rel~='icon']");
|
||||
|
||||
link.href = "/favicon_update.ico";
|
||||
link.type = "image/x-icon";
|
||||
}
|
||||
console.log("PLAYING SOUND!!");
|
||||
piecemoveSound.currentTime = Math.round(Math.random() * 4) * 0.5;
|
||||
piecemoveSound.play()
|
||||
.then(() => {
|
||||
setTimeout(() => {
|
||||
piecemoveSound.pause();
|
||||
}, 400);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Playback failed:", error);
|
||||
});
|
||||
oldBoard = structuredClone(board);
|
||||
}
|
||||
}
|
||||
|
||||
function setUI() {
|
||||
if (ready) {
|
||||
if (state == -1) {
|
||||
document.getElementById('stats').textCotnent = "";
|
||||
const now = new Date();
|
||||
const differenceMs = Math.abs(start_time.getTime() - now.getTime());
|
||||
const totalSeconds = Math.floor(differenceMs / 1000);
|
||||
|
||||
const minutes = Math.floor(totalSeconds / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
|
||||
const paddedMinutes = String(minutes).padStart(2, '0');
|
||||
const paddedSeconds = String(seconds).padStart(2, '0');
|
||||
|
||||
document.getElementById('clock').textCotnent = `${paddedMinutes}:${paddedSeconds}`;
|
||||
|
||||
if (UID === undefined || turn != color) {
|
||||
if (turn == 0) {
|
||||
document.getElementById('turn').textCotnent = "White's Turn";
|
||||
} else if (turn == 0) {
|
||||
document.getElementById('turn').textCotnent = "Black's Turn";
|
||||
}
|
||||
} else if (turn == color) {
|
||||
document.getElementById('turn').textCotnent = "Your Turn";
|
||||
}
|
||||
} else {
|
||||
document.getElementById('turn').textCotnent = "";
|
||||
if (state == 0) {
|
||||
document.getElementById('stats').textCotnent = "Tie";
|
||||
} else if (state == 1) {
|
||||
document.getElementById('stats').textCotnent = "White Won";
|
||||
} else if (state == 2) {
|
||||
document.getElementById('stats').textCotnent = "Black Won";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
document.getElementById('turn').textCotnent = "";
|
||||
document.getElementById('clock').textCotnent = "";
|
||||
document.getElementById('stats').textCotnent = "Waiting for Opponent";
|
||||
}
|
||||
}
|
||||
|
||||
canvas.addEventListener('mousemove', (e) => {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const x = ((e.clientX - rect.left) / rect.width) * width;
|
||||
@@ -131,7 +241,7 @@
|
||||
mouse = [x, y];
|
||||
i = Math.floor((x / width) * 8);
|
||||
j = Math.floor((y / height) * 8);
|
||||
if (ready) {
|
||||
if (ready && state == -1) {
|
||||
canvas.style.cursor = 'default';
|
||||
if (board) {
|
||||
if (color == 0) {
|
||||
@@ -157,7 +267,7 @@
|
||||
canvas.addEventListener('mouseup', (e) => {
|
||||
i = mHouse[0];
|
||||
j = mHouse[1];
|
||||
if (ready && board) {
|
||||
if (state == -1 && ready && board) {
|
||||
if (board[j][i] !== "E" && (board[j][i] === board[j][i].toUpperCase() ? 0 : 1) === color && turn == color) {
|
||||
|
||||
selected = [i, j];
|
||||
@@ -192,6 +302,7 @@
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
board = data.board.grid;
|
||||
boardOnChange();
|
||||
console.log(data);
|
||||
selected = undefined;
|
||||
moves = [];
|
||||
@@ -216,6 +327,11 @@
|
||||
ready = data.ready;
|
||||
turn = data.turn;
|
||||
board = data.board.grid;
|
||||
boardOnChange();
|
||||
state = data.state;
|
||||
start_time = new Date(data.start_time);
|
||||
|
||||
setUI();
|
||||
})
|
||||
.catch((error) => console.error('Error:', error));
|
||||
}
|
||||
@@ -252,11 +368,14 @@
|
||||
color = data.color;
|
||||
ready = data.ready;
|
||||
turn = data.turn;
|
||||
state = data.state;
|
||||
start_time = new Date(data.start_time);
|
||||
}
|
||||
ROOM_ID = rid;
|
||||
board = data.board.grid;
|
||||
numberMap = "12345678";
|
||||
letterMap = "abcdefgh";
|
||||
setUI();
|
||||
document.getElementById('join-menu').outerHTML = '';
|
||||
const urlID = extractIdFromPath();
|
||||
if (urlID == null) {
|
||||
|
||||
BIN
static/endgame.mp3
Normal file
BIN
static/endgame.mp3
Normal file
Binary file not shown.
BIN
static/piecemove.mp3
Normal file
BIN
static/piecemove.mp3
Normal file
Binary file not shown.
Reference in New Issue
Block a user