ssg, !ssr, classes, board init

This commit is contained in:
sparshg
2024-09-14 03:02:14 +05:30
parent 0649cf1cdc
commit 73ab2b63a8
12 changed files with 114 additions and 46 deletions

11
app/package-lock.json generated
View File

@@ -12,6 +12,7 @@
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.4",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.6",
"@types/eslint": "^9.6.0",
@@ -988,6 +989,16 @@
"@sveltejs/kit": "^2.0.0"
}
},
"node_modules/@sveltejs/adapter-static": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.4.tgz",
"integrity": "sha512-Qm4GAHCnRXwfWG9/AtnQ7mqjyjTs7i0Opyb8H2KH9rMR7fLxqiPx/tXeoE6HHo66+72CjyOb4nFH3lrejY4vzA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@sveltejs/kit": "^2.0.0"
}
},
"node_modules/@sveltejs/kit": {
"version": "2.5.26",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.26.tgz",

View File

@@ -13,6 +13,7 @@
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.4",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.6",
"@types/eslint": "^9.6.0",

View File

@@ -1,21 +1,27 @@
<script lang="ts">
import Cell from '$lib/cell.svelte';
import type { Board } from '$lib/state.svelte';
import { Board } from '$lib/state.svelte';
import { Crosshair, Ship } from 'lucide-svelte';
const callback = (i: number, j: number) => {
if (!isOpponent) return;
console.log(`Cell clicked at row ${i}, column ${j}`);
board[i][j] = 'hit';
};
let board: Board = $state(Array(10).fill(Array(10).fill('empty')));
let { isOpponent } = $props<{ isOpponent: boolean }>();
let { board, callback }: { board: Board; callback: (i: number, j: number) => void } = $props();
</script>
<div class="grid grid-cols-10 gap-1 bg-blue-100 p-2 rounded-lg">
{#each board as row, i}
{#each board.board as row, i}
{#each row as cell, j}
<Cell {cell} {isOpponent} callback={() => callback(i, j)} />
<button
class="aspect-square bg-blue-200 flex items-center justify-center {!board.isOpponent
? 'cursor-default'
: ''}"
onclick={() => callback(i, j)}
>
{#if cell === 's'}
<Ship class="size-3/5 text-blue-500" />
{:else if cell === 'h'}
<Crosshair class="size-3/5 text-red-500" />
{:else if cell === 'm'}
<div class="size-3/5 bg-gray-300 rounded-full"></div>
{/if}
</button>
{/each}
{/each}
</div>

View File

@@ -1,19 +0,0 @@
<script lang="ts">
import { Ship, Crosshair } from 'lucide-svelte';
let { cell, isOpponent, callback } = $props<{
cell: string;
isOpponent: boolean;
callback: () => void;
}>();
</script>
<button class="aspect-square bg-blue-200 flex items-center justify-center" onclick={callback}>
{#if cell === 'ship' && !isOpponent}
<Ship class="size-3/5 text-blue-500" />
{:else if cell === 'hit'}
<Crosshair class="size-3/5 text-red-500" />
{:else if cell === 'miss'}
<div class="size-3/5 bg-gray-300 rounded-full"></div>
{/if}
</button>

View File

@@ -2,7 +2,9 @@
import { Anchor } from 'lucide-svelte';
</script>
<header class="text-center mb-8">
<Anchor class="w-16 h-16 text-blue-600 mx-auto mb-4" />
<header
class="mb-8 flex items-center w-fit rounded-full px-6 py-3 drop-shadow-md bg-gray-50 mx-auto"
>
<Anchor class="size-16 mr-8 text-blue-600" />
<h1 class="text-4xl font-bold text-gray-900">Battleship Online</h1>
</header>

View File

@@ -1,8 +1,53 @@
export type Phase = 'placement' | 'battle' | 'gameover';
export type CellType = 'empty' | 'ship' | 'hit' | 'miss';
export type Board = Array<Array<CellType>>;
export type CellType = 'e' | 's' | 'h' | 'm'; // empty, ship, hit, miss
export class State {
phase: Phase = $state('placement');
playerBoard = $state(new Board(false));
opponentBoard = $state(new Board(true));
}
export class Board {
static shipTypes = [5, 4, 3, 3, 2];
board: Array<Array<CellType>> = $state(Array.from({ length: 10 }, () => Array.from({ length: 10 }, () => 'e')));
isOpponent: boolean = false;
constructor(isOpponent: boolean) {
this.isOpponent = isOpponent;
if (!isOpponent) this.randomize();
}
set(x: number, y: number, type: CellType) {
this.board[x][y] = type;
}
randomize() {
this.board = Array.from({ length: 10 }, () => Array.from({ length: 10 }, () => 'e'));
for (const shipLength of Board.shipTypes) {
while (true) {
const dir = Math.round(Math.random());
const x = Math.floor(Math.random() * (dir ? 10 : 11 - shipLength));
const y = Math.floor(Math.random() * (dir ? (11 - shipLength) : 10));
if (this.isOverlapping(x, y, shipLength, dir)) continue;
for (let i = 0; i < shipLength; i++) {
this.board[dir ? x : x + i][dir ? y + i : y] = 's';
}
break;
}
}
}
isOverlapping(x: number, y: number, length: number, dir: number): boolean {
for (let i = -1; i < 2; i++) {
for (let j = -1; j < length + 1; j++) {
let [tx, ty] = [x + (dir ? i : j), y + (dir ? j : i)];
if (tx < 0 || tx >= 10 || ty < 0 || ty >= 10) continue;
if (this.board[tx][ty] != 'e') return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,2 @@
export const prerender = true;
export const ssr = false;

View File

@@ -25,17 +25,22 @@
<div class="grid md:grid-cols-2 gap-8">
<div>
<h3 class="text-lg font-medium text-gray-700 mb-2">Your Board</h3>
<Board isOpponent={false} />
<Board board={gameState.playerBoard} callback={() => {}} />
</div>
<div>
<h3 class="text-lg font-medium text-gray-700 mb-2">Opponent's Board</h3>
<Board isOpponent={true} />
<Board
board={gameState.opponentBoard}
callback={(i, j) => gameState.opponentBoard.set(i, j, 'h')}
/>
</div>
</div>
<div class="flex justify-center space-x-4">
{#if gameState.phase === 'placement'}
<button class="btn btn-primary">Rotate Ship</button>
<button class="btn btn-primary" onclick={() => gameState.playerBoard.randomize()}
>Randomize</button
>
{:else}
<button class="btn btn-primary">Fire!</button>
{/if}

View File

@@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-auto';
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
@@ -6,12 +6,16 @@ const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
adapter: adapter({
// default options are shown. On some platforms
// these options are set automatically — see below
pages: 'build',
assets: 'build',
fallback: undefined,
precompress: false,
strict: true
})
}
};