diff --git a/client/src/lib/Dialog.svelte b/client/src/lib/Dialog.svelte new file mode 100644 index 0000000..fd46293 --- /dev/null +++ b/client/src/lib/Dialog.svelte @@ -0,0 +1,34 @@ + + +{#if visible} +
visible = false} on:keydown={keydown}> +
+
+ +
+{/if} + + \ No newline at end of file diff --git a/client/src/lib/RulesDialog.svelte b/client/src/lib/RulesDialog.svelte new file mode 100644 index 0000000..6fe19e4 --- /dev/null +++ b/client/src/lib/RulesDialog.svelte @@ -0,0 +1,96 @@ + + + +
+ +
+

Rules

+

How do I play the game?

+

You choose where your opponent will play

+

The board is divided into sub-boards, and you can always only play in one of them. Based on where you place your + piece, the opponent will have to play in the corresponding sub-board. +

+

On desktop, you can move your mouse over the field to see where the opponent "will be sent".

+ +
+
+ +
+
+ +
+
+

When you can't play in the target board, you'll stay in the current board. If you can't stay, you'll play in the board the previous move was made in. + You can see move list on the right to see where you might get sent. Mouse preview shows this correctly. +

+

Three in a row to win a board

+

Get 3 in a row/column/diagonally in a sub-board to with that sub-board. A larger symbol will be drawn.

+ +
+
+ +
+
+ +
+
+

Three boards in a row to win the game

+

Get 3 sub-boards in a row/column/diagonally to win the overall game.

+ +
+
+ +
+
+ +
+
+
+ + \ No newline at end of file diff --git a/client/src/lib/game.svelte b/client/src/lib/game.svelte index 95611b9..a00f074 100644 --- a/client/src/lib/game.svelte +++ b/client/src/lib/game.svelte @@ -10,6 +10,13 @@ export var twoPlayer: boolean = false; export var selfName: string | null = null; export var opponentName: string | null = null; + export var readonly: boolean = false; + export var defaultHighlightedContainer: number | null = null; + export var defaultHoveredPiece: { i: number, j: number } | null = null; + export var autoCalculateState: boolean = true; + export var showMoveList: boolean = true; + export var innerWidthOverride: number | null = null; + export var innerHeightOverride: number | null = null; const dispatch = createEventDispatcher(); @@ -25,11 +32,11 @@ 'bottom right' ]; - var hoveredPiece: null | { i: number, j: number } = null; + var hoveredPiece: null | { i: number, j: number } = defaultHoveredPiece; - var highlightedContainer: null | number = null; + var highlightedContainer: null | number = defaultHighlightedContainer; - $: highlightedContainer = hoveredPiece ? highlightContainerByPiece(hoveredPiece.j) : null; + $: highlightedContainer = hoveredPiece ? highlightContainerByPiece(hoveredPiece.j) : defaultHighlightedContainer; function highlightContainerByPiece(j: number) { if(!containerStates[j]) return j; @@ -47,7 +54,7 @@ var currentContainer: number = 4; var currentPlayer: 1 | 2 = 1; - var moves: { p: 1 | 2, i: number, j: number }[] = []; + export var moves: { p: 1 | 2, i: number, j: number }[] = []; $: currentContainer = getCurrentContainer(moves); $: currentPlayer = moves[moves.length - 1]?.p == 1 ? 2 : 1; @@ -61,10 +68,11 @@ return backtrack() ?? -1; } - let containerStates = new Array(9).fill(0); - let overallState = 0; + export let containerStates = new Array(9).fill(0); + export let overallState = 0; function addMove(i: number, j: number) { + if(readonly) return; if(moves.find(move => move.i == i && move.j == j)) return; if(currentContainer !== i) return; @@ -97,6 +105,7 @@ export { addPlayerMove }; function updateContainerStates() { + if(!autoCalculateState) return; for(var i in containerStates) { if(containerStates[i]) continue; var containerMoves = moves.filter(move => move.i === Number(i)); @@ -153,6 +162,7 @@ function reset() { moves = []; containerStates = new Array(9).fill(0); + overallState = 0; } function check(e: MouseEvent) { @@ -186,15 +196,24 @@ return () => clearTimeout(i); }); - let innerWidth = typeof window !== "undefined" ? window.innerWidth : 0; - let innerHeight = typeof window !== "undefined" ? window.innerHeight : 0; + let winnerWidth = typeof window !== "undefined" ? window.innerWidth : 0; + let winnerHeight = typeof window !== "undefined" ? window.innerHeight : 0; + let innerWidth = innerWidthOverride || winnerWidth; + let innerHeight = innerHeightOverride || winnerHeight; + + $: innerWidth = innerWidthOverride || winnerWidth; + $: innerHeight = innerHeightOverride || winnerHeight; + + updateContainerStates(); - hoveredPiece = null} /> + hoveredPiece = defaultHoveredPiece} /> +{#if !readonly} +{/if} -{#if !twoPlayer} +{#if !twoPlayer && !readonly}
{/if} -{#if innerWidth < 1024} +{#if innerWidth < 1024 && showMoveList} {/if} -
+
{#each classes as className, i}
{ if(currentContainer == i) hoveredPiece = { i, j } }} - - on:mouseleave={() => { if(hoveredPiece?.i == i && hoveredPiece.j == j) hoveredPiece = null; }} + + on:mouseleave={() => { if(hoveredPiece?.i == i && hoveredPiece.j == j) hoveredPiece = defaultHoveredPiece; }} > @@ -367,8 +386,8 @@ {/key}
- - {#if movesShown || innerWidth >= 1024 || innerWidth / innerHeight > 1.4} + + {#if showMoveList && (movesShown || innerWidth >= 1024 || innerWidth / innerHeight > 1.4)}
1.4} class="lg:hidden bg-black/40 fixed inset-0 z-10" on:click={() => movesShown = false} on:keydown={() => movesShown = false} />
@@ -395,7 +414,7 @@
{/if} {#each moves as move} - hoveredPiece = { i: move.i, j: move.j }} on:mouseout={() => { if(hoveredPiece?.j == move.j && hoveredPiece.i == move.i) hoveredPiece = null }} /> + hoveredPiece = { i: move.i, j: move.j }} on:mouseout={() => { if(hoveredPiece?.j == move.j && hoveredPiece.i == move.i) hoveredPiece = defaultHoveredPiece }} /> {/each}
@@ -404,8 +423,10 @@
- -