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

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/target /target
.vscode

11
app/package-lock.json generated
View File

@@ -12,6 +12,7 @@
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.4",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.6", "@sveltejs/vite-plugin-svelte": "^4.0.0-next.6",
"@types/eslint": "^9.6.0", "@types/eslint": "^9.6.0",
@@ -988,6 +989,16 @@
"@sveltejs/kit": "^2.0.0" "@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": { "node_modules/@sveltejs/kit": {
"version": "2.5.26", "version": "2.5.26",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.26.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.26.tgz",

View File

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

View File

@@ -1,21 +1,27 @@
<script lang="ts"> <script lang="ts">
import Cell from '$lib/cell.svelte'; import { Board } from '$lib/state.svelte';
import type { Board } from '$lib/state.svelte'; import { Crosshair, Ship } from 'lucide-svelte';
const callback = (i: number, j: number) => { let { board, callback }: { board: Board; callback: (i: number, j: number) => void } = $props();
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 }>();
</script> </script>
<div class="grid grid-cols-10 gap-1 bg-blue-100 p-2 rounded-lg"> <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} {#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}
{/each} {/each}
</div> </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'; import { Anchor } from 'lucide-svelte';
</script> </script>
<header class="text-center mb-8"> <header
<Anchor class="w-16 h-16 text-blue-600 mx-auto mb-4" /> 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> <h1 class="text-4xl font-bold text-gray-900">Battleship Online</h1>
</header> </header>

View File

@@ -1,8 +1,53 @@
export type Phase = 'placement' | 'battle' | 'gameover'; export type Phase = 'placement' | 'battle' | 'gameover';
export type CellType = 'empty' | 'ship' | 'hit' | 'miss'; export type CellType = 'e' | 's' | 'h' | 'm'; // empty, ship, hit, miss
export type Board = Array<Array<CellType>>;
export class State { export class State {
phase: Phase = $state('placement'); 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 class="grid md:grid-cols-2 gap-8">
<div> <div>
<h3 class="text-lg font-medium text-gray-700 mb-2">Your Board</h3> <h3 class="text-lg font-medium text-gray-700 mb-2">Your Board</h3>
<Board isOpponent={false} /> <Board board={gameState.playerBoard} callback={() => {}} />
</div> </div>
<div> <div>
<h3 class="text-lg font-medium text-gray-700 mb-2">Opponent's Board</h3> <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> </div>
<div class="flex justify-center space-x-4"> <div class="flex justify-center space-x-4">
{#if gameState.phase === 'placement'} {#if gameState.phase === 'placement'}
<button class="btn btn-primary">Rotate Ship</button> <button class="btn btn-primary" onclick={() => gameState.playerBoard.randomize()}
>Randomize</button
>
{:else} {:else}
<button class="btn btn-primary">Fire!</button> <button class="btn btn-primary">Fire!</button>
{/if} {/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'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
@@ -6,12 +6,16 @@ const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors // Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors // for more information about preprocessors
preprocess: vitePreprocess(), preprocess: vitePreprocess(),
kit: { kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. adapter: adapter({
// If your environment is not supported, or you settled on a specific environment, switch out the adapter. // default options are shown. On some platforms
// See https://kit.svelte.dev/docs/adapters for more information about adapters. // these options are set automatically — see below
adapter: adapter() pages: 'build',
assets: 'build',
fallback: undefined,
precompress: false,
strict: true
})
} }
}; };

8
src/game.rs Normal file
View File

@@ -0,0 +1,8 @@
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Board([[Option<char>; 10]; 10]);
impl Board {
// pub async fn new
}

View File

@@ -1,4 +1,6 @@
use axum::{routing::get, Router}; mod game;
use axum::{routing::get, Json, Router};
use tokio::net::TcpListener; use tokio::net::TcpListener;
#[tokio::main] #[tokio::main]