diff --git a/Makefile.toml b/Makefile.toml
index c7089b8..b253ed9 100644
--- a/Makefile.toml
+++ b/Makefile.toml
@@ -6,3 +6,8 @@ args = ["build"]
command = "cargo"
args = ["build", "--release"]
dependencies = ["build-ui"]
+
+[tasks.run-release]
+command = "cargo"
+args = ["run", "--release"]
+dependencies = ["build-ui"]
diff --git a/index.html b/index.html
index b7820db..e9c08c5 100644
--- a/index.html
+++ b/index.html
@@ -8,7 +8,7 @@
-
+
diff --git a/package.json b/package.json
index b511b78..bd5a994 100644
--- a/package.json
+++ b/package.json
@@ -13,15 +13,18 @@
"license": "ISC",
"devDependencies": {
"@types/node": "^18.11.7",
+ "@types/uuid": "^8.3.4",
"typescript": "^4.8.4",
"vite": "^3.2.1",
"vite-plugin-solid": "^2.3.10"
},
"dependencies": {
"@fontsource/noto-sans-symbols-2": "^4.5.10",
+ "@fontsource/ubuntu": "^4.5.11",
"@solid-primitives/websocket": "^0.3.3",
"solid-devtools": "^0.20.1",
"solid-js": "^1.6.0",
+ "uuid": "^9.0.0",
"zod": "^3.19.1"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c5d191b..fa7ff1f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2,24 +2,30 @@ lockfileVersion: 5.4
specifiers:
'@fontsource/noto-sans-symbols-2': ^4.5.10
+ '@fontsource/ubuntu': ^4.5.11
'@solid-primitives/websocket': ^0.3.3
'@types/node': ^18.11.7
+ '@types/uuid': ^8.3.4
solid-devtools: ^0.20.1
solid-js: ^1.6.0
typescript: ^4.8.4
+ uuid: ^9.0.0
vite: ^3.2.1
vite-plugin-solid: ^2.3.10
zod: ^3.19.1
dependencies:
'@fontsource/noto-sans-symbols-2': 4.5.10
+ '@fontsource/ubuntu': 4.5.11
'@solid-primitives/websocket': 0.3.3_solid-js@1.6.0
solid-devtools: 0.20.1_solid-js@1.6.0+vite@3.2.1
solid-js: 1.6.0
+ uuid: 9.0.0
zod: 3.19.1
devDependencies:
'@types/node': 18.11.7
+ '@types/uuid': 8.3.4
typescript: 4.8.4
vite: 3.2.1
vite-plugin-solid: 2.3.10_solid-js@1.6.0+vite@3.2.1
@@ -335,6 +341,10 @@ packages:
resolution: {integrity: sha512-3sDgCDm3zNijKoQ4UVSg5PgGk2Drp9QIF73RKZzfiZ4Ksgsu6h4XXng2i+f6cXHpSaP2CEOLgTu7EwH298djMg==}
dev: false
+ /@fontsource/ubuntu/4.5.11:
+ resolution: {integrity: sha512-c+B8A8r31hJLk8Ek7Svoz4GwjtInhc60LFM6XTr5TKtZbcct8TPNxJuKX4+hnqoWgcvy8s0Gj4mde4TcK/SgDQ==}
+ dev: false
+
/@jridgewell/gen-mapping/0.1.1:
resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==}
engines: {node: '>=6.0.0'}
@@ -566,6 +576,10 @@ packages:
resolution: {integrity: sha512-LhFTglglr63mNXUSRYD8A+ZAIu5sFqNJ4Y2fPuY7UlrySJH87rRRlhtVmMHplmfk5WkoJGmDjE9oiTfyX94CpQ==}
dev: true
+ /@types/uuid/8.3.4:
+ resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==}
+ dev: true
+
/ansi-styles/3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
@@ -1025,6 +1039,11 @@ packages:
escalade: 3.1.1
picocolors: 1.0.0
+ /uuid/9.0.0:
+ resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
+ hasBin: true
+ dev: false
+
/vite-plugin-solid/2.3.10_solid-js@1.6.0+vite@3.2.1:
resolution: {integrity: sha512-5jMF+QFk2TQaPLlDl7LvZZ99D4kO1X+rD9LR78p9sx9O+XisVSQaHFPLrCsyW/lXuBwub+ox/pNaZdCUZJwd3Q==}
peerDependencies:
diff --git a/src-web/App.tsx b/src-web/App.tsx
index 102129e..7eeca2f 100644
--- a/src-web/App.tsx
+++ b/src-web/App.tsx
@@ -1,9 +1,9 @@
-import { Component, createEffect, createSignal, Match, Switch } from 'solid-js';
+import { Component, createSignal, Match, Show, Switch } from 'solid-js';
import createWebsocket from '@solid-primitives/websocket';
import { wsUrl } from './constants';
import Spinner from './components/Spinner';
import Welcome from './components/Welcome';
-import { Board as BoardData, Move, ServerChessEvent } from './events';
+import { Board as BoardData, Move, ServerChessEvent, Side } from './events';
import Board from './components/Board';
interface Props {
@@ -13,29 +13,29 @@ interface Props {
const App: Component = (props) => {
const [board, setBoard] = createSignal(Array(64).fill(null));
const [possibleMoves, setPossibleMoves] = createSignal([]);
+ const [side, setSide] = createSignal(null);
function handleEvent(e: MessageEvent) {
const data = JSON.parse(e.data);
const event = ServerChessEvent.parse(data);
if (event.event === 'BoardUpdate') {
- console.log(event.data.board);
setBoard(event.data.board);
} else if (event.event === 'PossibleMoves') {
- console.log(event.data.moves);
setPossibleMoves(event.data.moves);
+ } else if (event.event === 'StartGame') {
+ setSide(event.data.side);
}
}
- createEffect(() => {
- console.log('board is now', board());
- });
-
- createEffect(() => {
- console.log('moves is now', possibleMoves());
- });
-
const [connect, disconnect, send, state, socket] = createWebsocket(wsUrl(props.gameId), handleEvent, console.error);
+ function joinGame() {
+ connect();
+ setSide(null);
+ setBoard(Array(64).fill(null));
+ setPossibleMoves([]);
+ }
+
function makeMove(move: Move) {
send(JSON.stringify({
event: 'MakeMove',
@@ -45,15 +45,20 @@ const App: Component = (props) => {
}));
}
- return Hello, World!
>}>
+ return }>
- connect()} />
+ joinGame()} />
-
+
+ Waiting for opponent...
+
+ >}>
+
+
};
diff --git a/src-web/components/Button.css b/src-web/components/Button.css
index 478dfdd..5818d3d 100644
--- a/src-web/components/Button.css
+++ b/src-web/components/Button.css
@@ -1,10 +1,12 @@
.button {
width: 100%;
+ font-family: Ubuntu, sans-serif;
font-size: 1.35rem;
padding: 8px;
border-radius: 8px;
- border: 1px solid black;
- background-color: #ddd;
+ border: 2px solid var(--accent);
+ background-color: var(--accent-dim);
+ color: white;
}
.button:hover {
@@ -14,3 +16,10 @@
.button:active {
filter: brightness(.8);
}
+
+.button-container {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ width: 100%;
+}
diff --git a/src-web/components/Button.tsx b/src-web/components/Button.tsx
index b84a833..71081d0 100644
--- a/src-web/components/Button.tsx
+++ b/src-web/components/Button.tsx
@@ -14,3 +14,11 @@ const Button: Component = (props) => {
}
export default Button;
+
+export const ButtonContainer: Component<{ children: JSX.Element }> = (props) => {
+ const c = children(() => props.children);
+
+ return
+ {c()}
+
+}
diff --git a/src-web/components/Spinner.tsx b/src-web/components/Spinner.tsx
index bbdcdda..e34f1e8 100644
--- a/src-web/components/Spinner.tsx
+++ b/src-web/components/Spinner.tsx
@@ -9,7 +9,7 @@ interface Props {
const Spinner: Component = (props) => {
return
}
diff --git a/src-web/components/Welcome.tsx b/src-web/components/Welcome.tsx
index c21a443..f797116 100644
--- a/src-web/components/Welcome.tsx
+++ b/src-web/components/Welcome.tsx
@@ -1,5 +1,5 @@
import { Component } from "solid-js";
-import Button from "./Button";
+import Button, { ButtonContainer } from "./Button";
interface Props {
gameId: string;
@@ -7,16 +7,29 @@ interface Props {
}
const Welcome: Component = (props) => {
- console.log(props.gameId);
- return
+ function shareInvite() {
+ navigator.share({
+ url: window.location.href,
+ });
+ }
+
+ return <>
Welcome
-
- Game ID:
{props.gameId}
-
-
- ;
+
+ Game ID:
+ {props.gameId}
+
+
+
+
+
+
+
+ >;
}
export default Welcome;
diff --git a/src-web/constants.ts b/src-web/constants.ts
index 2bd22b5..12d6e8d 100644
--- a/src-web/constants.ts
+++ b/src-web/constants.ts
@@ -1,8 +1,8 @@
// const WS_BASE = 'ws://localhost:3000/ws/';
export function wsUrl(id: string) {
- if (import.meta.env.WS_BASE) {
- return import.meta.env.WS_BASE + id;
+ if (import.meta.env.VITE_WS_BASE) {
+ return import.meta.env.VITE_WS_BASE + id;
} else {
const loc = window.location;
let newUri = loc.protocol === "https:" ? "wss://" : "ws://";
diff --git a/src-web/events.ts b/src-web/events.ts
index 4159bff..b939545 100644
--- a/src-web/events.ts
+++ b/src-web/events.ts
@@ -51,9 +51,16 @@ export const PossibleMovesEvent = z.object({
export type PossibleMovesEvent = z.infer;
+export const StartGameEvent = z.object({
+ side: Side,
+});
+
+export type StartGameEvent = z.infer;
+
export const ServerChessEvent = z.discriminatedUnion("event", [
z.object({ event: z.literal('BoardUpdate'), data: BoardUpdateEvent }),
z.object({ event: z.literal('PossibleMoves'), data: PossibleMovesEvent }),
+ z.object({ event: z.literal('StartGame'), data: StartGameEvent })
]);
export type ServerChessEvent = z.infer;
diff --git a/src-web/index.tsx b/src-web/index.tsx
index e602e6b..71db148 100644
--- a/src-web/index.tsx
+++ b/src-web/index.tsx
@@ -4,11 +4,20 @@ import 'solid-devtools';
import App from './App';
import './main.css';
+import '@fontsource/ubuntu';
import "@fontsource/noto-sans-symbols-2";
+import { v4 as uuidv4 } from 'uuid';
+import Button from './components/Button';
const search = new URLSearchParams(window.location.search);
-if (search.has('game_id')) {
- render(() => , document.getElementById('root') as HTMLElement);
-} else {
- console.error('no game id');
+const root = document.getElementById('root')!;
+
+function newGame() {
+ window.location.search = '?game_id=' + uuidv4();
+}
+
+if (search.has('game_id')) {
+ render(() => , root);
+} else {
+ render(() => , root);
}
diff --git a/src-web/main.css b/src-web/main.css
index a620c0d..34a6f31 100644
--- a/src-web/main.css
+++ b/src-web/main.css
@@ -1,3 +1,14 @@
+:root {
+ --background: #13092b;
+ --background-transparent: #13092baa;
+ --background-2: #090f2b;
+ --foreground: #ddd;
+ --foreground-bright: #fff;
+ --foreground-dim: #aaa;
+ --accent: #f9027a;
+ --accent-dim: hsl(331, 50%, 49%);
+}
+
html {
box-sizing: border-box;
}
@@ -5,6 +16,10 @@ html {
html, body {
padding: 0;
margin: 0;
+ font-family: Ubuntu, sans-serif;
+ font-size: 16px;
+ background-color: var(--background);
+ color: var(--foreground);
}
* {
@@ -17,3 +32,10 @@ body {
width: 100%;
min-height: 100vh;
}
+
+#root {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
diff --git a/src/game.rs b/src/game.rs
index 37ad9f9..c60dae6 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -4,10 +4,10 @@ use uuid::Uuid;
use xtra::{prelude::*, WeakAddress};
use crate::{
- chess::{mv::generate_legal, Side},
+ chess::{mv::generate_legal},
constants::START_FEN,
player::{IncomingPlayerEvent, OutgoingPlayerEvent, Player},
- prelude::Board,
+ prelude::*,
};
#[derive(Actor, Default)]
@@ -80,6 +80,7 @@ impl Handler for ChessGame {
None
} else {
self.black = Some(join_game.player);
+ self.send_game_start();
self.broadcast_new_board();
self.send_possible_moves();
Some(Side::Black)
@@ -128,18 +129,28 @@ impl ChessGame {
}
}
- fn broadcast_new_board(&self) {
+ fn send_game_start(&self) {
// TODO: Handle players disconnecting
- tokio::spawn(self.white.send(OutgoingPlayerEvent::BoardUpdate {
- board: self.board.board.to_vec(),
- }));
+ tokio::spawn(self.white.send(OutgoingPlayerEvent::StartGame { side: White }));
if let Some(black) = &self.black {
- tokio::spawn(black.send(OutgoingPlayerEvent::BoardUpdate {
- board: self.board.board.to_vec(),
- }));
+ tokio::spawn(black.send(OutgoingPlayerEvent::StartGame { side: Black }));
}
}
+ fn broadcast(&self, message: OutgoingPlayerEvent) {
+ // TODO: Handle players disconnecting
+ tokio::spawn(self.white.send(message.clone()));
+ if let Some(black) = &self.black {
+ tokio::spawn(black.send(message));
+ }
+ }
+
+ fn broadcast_new_board(&self) {
+ self.broadcast(OutgoingPlayerEvent::BoardUpdate {
+ board: self.board.board.to_vec(),
+ });
+ }
+
fn send_possible_moves(&self) {
let (current, other) = match &self.board.to_move {
Side::Black => (self.black.clone().unwrap(), self.white.clone()),
diff --git a/src/lib.rs b/src/lib.rs
index 99122a9..877b5c3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,6 +11,7 @@ pub mod prelude {
pub use crate::chess::Board;
pub use crate::chess::Piece;
+ pub use crate::chess::Side;
pub use crate::chess::PieceType::*;
pub use crate::chess::Side::*;
diff --git a/src/player.rs b/src/player.rs
index cbe6b42..6a02ce1 100644
--- a/src/player.rs
+++ b/src/player.rs
@@ -25,9 +25,10 @@ impl Player {
}
}
-#[derive(Debug, Serialize)]
+#[derive(Clone, Debug, Serialize)]
#[serde(tag = "event", content = "data")]
pub enum OutgoingPlayerEvent {
+ StartGame { side: Side },
BoardUpdate { board: Vec