Skip to content

Commit

Permalink
Board, pieces, orientations
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Nov 1, 2024
1 parent 4652920 commit b10f09b
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# 4stack

Tetrimino stacking client intended for practicing
Tetromino stacking client intended for practicing
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://json.schemastore.org/package.json",
"name": "4stack",
"version": "0.1.0",
"description": "Tetrimino stacking client intended for practicing",
"description": "Tetromino stacking client intended for practicing",
"scripts": {
"start": "vite --host 127.0.0.1",
"dev": "vite",
Expand All @@ -28,7 +28,10 @@
"vite-plugin-solid": "2.7.0"
},
"dependencies": {
"solid-js": "1.7.6"
"solid-js": "1.7.6",
"@vole-engine/core": "0.0.40",
"@vole-engine/draw": "0.0.1",
"rxjs": "7.8.1"
},
"packageManager": "[email protected]"
}
3 changes: 3 additions & 0 deletions src/component/app/App.module.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
:global .App {
canvas {
position: absolute;
}
}
210 changes: 208 additions & 2 deletions src/component/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,216 @@
import { type Component } from 'solid-js'
import { Engine, Vector, vec } from '@vole-engine/core'
import { Context } from '@vole-engine/draw'
import { Subscription } from 'rxjs'
import { type Component, onCleanup, onMount } from 'solid-js'
import './App.module.scss'

export const createOrientations = (desc: PieceDescription): PieceOrientationState => {
const orientation = { blocs: desc.blocs }
if (desc.rotationMode === 'off') return { orientations: [orientation, orientation, orientation, orientation] }
const offset = desc.rotationMode === 'between' ? 1 : 0
const cw = { blocs: orientation.blocs.map(b => rotateCw(b).add(Vector.Right.scale(offset))) }
const cw2 = { blocs: cw.blocs.map(b => rotateCw(b).add(Vector.Right.scale(offset))) }
const cw3 = { blocs: cw2.blocs.map(b => rotateCw(b).add(Vector.Right.scale(offset))) }

return { orientations: [orientation, cw, cw2, cw3] }
}

/**
* (1, 1)
* ...
* .o.
* ..x
*
* (-1, 1)
* ...
* .o.
* x..
*/
export const rotateCw = (position: Vector): Vector => vec(-position.y, position.x)

export type Board = Color[][]

/**
* 0 is empty
* 1 is garbage
* 2-n is a piece color
*/
export type Color = number

export type PieceDescription = {
blocs: Vector[]
rotationMode: 'normal' | 'between' | 'off'
}

export type Piece = {
blocs: Vector[]
}

export type PieceOrientationState = {
orientations: [Piece, Piece, Piece, Piece]
}

export type ActivePiece = {
pieceId: number
position: Vector
orientation: number
}

export type Input = {
left: Button
right: Button
ccw: Button
cw: Button
r180: Button
soft: Button
hard: Button
hold: Button
}

export type Button = {
held: boolean
pressed: boolean
released: boolean
}

export const piecesDescription: PieceDescription[] = [
{
// I piece
blocs: [vec(0, 0), vec(-1, 0), vec(1, 0), vec(2, 0)],
rotationMode: 'between'
},
{
// O piece
blocs: [vec(0, 0), vec(1, 0), vec(1, 1), vec(0, 1)],
rotationMode: 'off'
},
{
// T piece
blocs: [vec(0, 0), vec(0, -1), vec(1, 0), vec(-1, 0)],
rotationMode: 'normal'
},
{
// S piece
blocs: [vec(0, 0), vec(-1, 0), vec(0, -1), vec(1, -1)],
rotationMode: 'normal'
},
{
// Z piece
blocs: [vec(0, 0), vec(1, 0), vec(0, -1), vec(-1, -1)],
rotationMode: 'normal'
},
{
// J piece
blocs: [vec(0, 0), vec(-1, -1), vec(-1, 0), vec(1, 0)],
rotationMode: 'normal'
},
{
// L piece
blocs: [vec(0, 0), vec(1, -1), vec(1, 0), vec(-1, 0)],
rotationMode: 'normal'
}
]

export const pieces: PieceOrientationState[] = piecesDescription.map(createOrientations)

export const gameConfig = {
boardSize: vec(10, 20),
blockScreenSize: vec(40, 40),
colors: ['#111111', '#333333', 'cyan', 'yellow', 'purple', 'green', 'red', 'blue', 'orange']
}

export const App: Component = () => {
let canvas!: HTMLCanvasElement
let ctx!: Context
let engine!: Engine
const board: Board = []
let activePiece: ActivePiece | undefined
const subs: Subscription[] = []

const resizeWindow = (): void => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
}

const drawBoard = (board: Board): void => {
const screenSize = vec(canvas.width, canvas.height)
const screenCenter = screenSize.scale(0.5)
const blockSize = gameConfig.blockScreenSize
const boardSize = gameConfig.boardSize.scale(blockSize)
const gridOpts = { stroke: '#444444' }

for (let i = 0; i < gameConfig.boardSize.x; i++) {
for (let j = 0; j < gameConfig.boardSize.y; j++) {
ctx.rect(
blockSize.scale(vec(i, j)).add(blockSize.scale(0.5)).add(screenCenter).add(boardSize.scale(-0.5)),
blockSize,
gridOpts
)
}
}

// TODO: draw board
}

const drawActivePiece = (activePiece: ActivePiece): void => {
const screenSize = vec(canvas.width, canvas.height)
const screenCenter = screenSize.scale(0.5)
const blockSize = gameConfig.blockScreenSize
const boardSize = gameConfig.boardSize.scale(blockSize)
const opts = { fill: gameConfig.colors[activePiece.pieceId + 2], stroke: '#444444' }
const gridOffset = vec(Math.floor(gameConfig.boardSize.x / 2) - 1, -1)

pieces[activePiece.pieceId].orientations[activePiece.orientation].blocs.forEach(block => {
const position = activePiece.position
.add(block)
.add(gridOffset)
.scale(blockSize)
.add(blockSize.scale(0.5))
.add(screenCenter)
.add(boardSize.scale(-0.5))
ctx.rect(position, blockSize, opts)
})
}

onMount(() => {
ctx = new Context(canvas)
resizeWindow()
window.addEventListener('resize', resizeWindow)

engine = new Engine()
engine.start()
subs.push(
engine.eventDispatcher.beforeUpdate.subscribe(() => {
if (!activePiece) {
// TODO: piece selection
const pieceId = 0
activePiece = { pieceId, position: vec(0, 0), orientation: 0 }
}
if (engine.frameInfo.id % 10 === 0) {
activePiece.orientation = (activePiece.orientation + 1) % 4
}
if (engine.frameInfo.id % 40 === 0) {
activePiece.pieceId = (activePiece.pieceId + 1) % 7
activePiece.orientation = 0
}
})
)
subs.push(
engine.eventDispatcher.beforeDraw.subscribe(() => {
ctx.clear()
drawBoard(board)
drawActivePiece(activePiece!)
})
)
})

onCleanup(() => {
subs.forEach(s => s.unsubscribe())
})

return (
<div class="App">
<p>Henlo</p>
<canvas ref={canvas}></canvas>
</div>
)
}
4 changes: 1 addition & 3 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ export default defineConfig({
},
plugins: [solidPlugin()],
server: {
port: 3000,
hmr: false,
watch: undefined
port: 3000
},
build: {
target: 'esnext'
Expand Down

0 comments on commit b10f09b

Please sign in to comment.