ssg, !ssr, classes, board init
This commit is contained in:
11
app/package-lock.json
generated
11
app/package-lock.json
generated
@@ -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",
|
||||
|
@@ -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",
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
@@ -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>
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
2
app/src/routes/+layout.ts
Normal file
2
app/src/routes/+layout.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const prerender = true;
|
||||
export const ssr = false;
|
@@ -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}
|
||||
|
@@ -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
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user