add linting, do more type hinting
This commit is contained in:
parent
46f3fc7480
commit
d4f02c10df
29 changed files with 3353 additions and 1354 deletions
|
|
@ -1,172 +1,31 @@
|
|||
import Geometry, { Point, Rect } from './Geometry'
|
||||
import Protocol from './Protocol'
|
||||
import { Rng, RngSerialized } from './Rng'
|
||||
import { Rng } from './Rng'
|
||||
import Time from './Time'
|
||||
import { FixedLengthArray } from './Types'
|
||||
import Util from './Util'
|
||||
|
||||
export type Timestamp = number
|
||||
|
||||
export type EncodedPlayer = FixedLengthArray<[
|
||||
string,
|
||||
number,
|
||||
number,
|
||||
0|1,
|
||||
string|null,
|
||||
string|null,
|
||||
string|null,
|
||||
number,
|
||||
Timestamp,
|
||||
]>
|
||||
|
||||
export type EncodedPiece = FixedLengthArray<[
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
string|number,
|
||||
number,
|
||||
]>
|
||||
|
||||
export type EncodedPieceShape = number
|
||||
|
||||
export type EncodedGame = FixedLengthArray<[
|
||||
string,
|
||||
string,
|
||||
RngSerialized,
|
||||
import {
|
||||
Change,
|
||||
EncodedPiece,
|
||||
EvtInfo,
|
||||
Game,
|
||||
Input,
|
||||
Piece,
|
||||
PieceChange,
|
||||
Player,
|
||||
PlayerChange,
|
||||
Puzzle,
|
||||
Array<EncodedPlayer>,
|
||||
Record<string, EvtInfo>,
|
||||
PuzzleData,
|
||||
PuzzleDataChange,
|
||||
ScoreMode,
|
||||
]>
|
||||
|
||||
export interface ReplayData {
|
||||
log: any[],
|
||||
game: EncodedGame|null
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
id: number
|
||||
slug: string
|
||||
title: string
|
||||
}
|
||||
|
||||
interface GameRng {
|
||||
obj: Rng
|
||||
type?: string
|
||||
}
|
||||
|
||||
export interface Game {
|
||||
id: string
|
||||
players: Array<EncodedPlayer>
|
||||
puzzle: Puzzle
|
||||
evtInfos: Record<string, EvtInfo>
|
||||
scoreMode?: ScoreMode
|
||||
rng: GameRng
|
||||
}
|
||||
|
||||
export interface Image {
|
||||
id: number
|
||||
filename: string
|
||||
file: string
|
||||
url: string
|
||||
title: string
|
||||
tags: Array<Tag>
|
||||
created: number
|
||||
}
|
||||
|
||||
export interface GameSettings {
|
||||
tiles: number
|
||||
image: Image
|
||||
scoreMode: ScoreMode
|
||||
}
|
||||
|
||||
export interface Puzzle {
|
||||
tiles: Array<EncodedPiece>
|
||||
data: PuzzleData
|
||||
info: PuzzleInfo
|
||||
}
|
||||
|
||||
interface PuzzleData {
|
||||
started: number
|
||||
finished: number
|
||||
maxGroup: number
|
||||
maxZ: number
|
||||
}
|
||||
|
||||
interface PuzzleTable {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
enum PieceEdge {
|
||||
Flat = 0,
|
||||
Out = 1,
|
||||
In = -1,
|
||||
}
|
||||
export interface PieceShape {
|
||||
top: PieceEdge
|
||||
bottom: PieceEdge
|
||||
left: PieceEdge
|
||||
right: PieceEdge
|
||||
}
|
||||
|
||||
export interface Piece {
|
||||
owner: string|number
|
||||
idx: number
|
||||
pos: Point
|
||||
z: number
|
||||
group: number
|
||||
}
|
||||
|
||||
export interface PuzzleInfo {
|
||||
table: PuzzleTable
|
||||
targetTiles: number,
|
||||
imageUrl: string
|
||||
|
||||
width: number
|
||||
height: number
|
||||
tileSize: number
|
||||
tileDrawSize: number
|
||||
tileMarginWidth: number
|
||||
tileDrawOffset: number
|
||||
snapDistance: number
|
||||
|
||||
tiles: number
|
||||
tilesX: number
|
||||
tilesY: number
|
||||
|
||||
shapes: Array<EncodedPieceShape>
|
||||
}
|
||||
|
||||
export interface Player {
|
||||
id: string
|
||||
x: number
|
||||
y: number
|
||||
d: 0|1
|
||||
name: string|null
|
||||
color: string|null
|
||||
bgcolor: string|null
|
||||
points: number
|
||||
ts: Timestamp
|
||||
}
|
||||
|
||||
interface EvtInfo {
|
||||
_last_mouse: Point|null
|
||||
_last_mouse_down: Point|null
|
||||
}
|
||||
|
||||
export enum ScoreMode {
|
||||
FINAL = 0,
|
||||
ANY = 1,
|
||||
}
|
||||
Timestamp
|
||||
} from './Types'
|
||||
import Util from './Util'
|
||||
|
||||
const IDLE_TIMEOUT_SEC = 30
|
||||
|
||||
// Map<gameId, Game>
|
||||
const GAMES: Record<string, Game> = {}
|
||||
|
||||
function exists(gameId: string) {
|
||||
function exists(gameId: string): boolean {
|
||||
return (!!GAMES[gameId]) || false
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +49,7 @@ function setGame(gameId: string, game: Game): void {
|
|||
|
||||
function getPlayerIndexById(gameId: string, playerId: string): number {
|
||||
let i = 0;
|
||||
for (let player of GAMES[gameId].players) {
|
||||
for (const player of GAMES[gameId].players) {
|
||||
if (Util.decodePlayer(player).id === playerId) {
|
||||
return i
|
||||
}
|
||||
|
|
@ -293,8 +152,8 @@ function getAllPlayers(gameId: string): Array<Player> {
|
|||
: []
|
||||
}
|
||||
|
||||
function get(gameId: string) {
|
||||
return GAMES[gameId]
|
||||
function get(gameId: string): Game|null {
|
||||
return GAMES[gameId] || null
|
||||
}
|
||||
|
||||
function getPieceCount(gameId: string): number {
|
||||
|
|
@ -319,7 +178,7 @@ function isFinished(gameId: string): boolean {
|
|||
|
||||
function getFinishedPiecesCount(gameId: string): number {
|
||||
let count = 0
|
||||
for (let t of GAMES[gameId].puzzle.tiles) {
|
||||
for (const t of GAMES[gameId].puzzle.tiles) {
|
||||
if (Util.decodePiece(t).owner === -1) {
|
||||
count++
|
||||
}
|
||||
|
|
@ -335,29 +194,33 @@ function getPiecesSortedByZIndex(gameId: string): Piece[] {
|
|||
function changePlayer(
|
||||
gameId: string,
|
||||
playerId: string,
|
||||
change: any
|
||||
change: PlayerChange
|
||||
): void {
|
||||
const player = getPlayer(gameId, playerId)
|
||||
if (player === null) {
|
||||
return
|
||||
}
|
||||
|
||||
for (let k of Object.keys(change)) {
|
||||
for (const k of Object.keys(change)) {
|
||||
// @ts-ignore
|
||||
player[k] = change[k]
|
||||
}
|
||||
setPlayer(gameId, playerId, player)
|
||||
}
|
||||
|
||||
function changeData(gameId: string, change: any): void {
|
||||
for (let k of Object.keys(change)) {
|
||||
function changeData(gameId: string, change: PuzzleDataChange): void {
|
||||
for (const k of Object.keys(change)) {
|
||||
// @ts-ignore
|
||||
GAMES[gameId].puzzle.data[k] = change[k]
|
||||
}
|
||||
}
|
||||
|
||||
function changeTile(gameId: string, pieceIdx: number, change: any): void {
|
||||
for (let k of Object.keys(change)) {
|
||||
function changePiece(
|
||||
gameId: string,
|
||||
pieceIdx: number,
|
||||
change: PieceChange
|
||||
): void {
|
||||
for (const k of Object.keys(change)) {
|
||||
const piece = Util.decodePiece(GAMES[gameId].puzzle.tiles[pieceIdx])
|
||||
// @ts-ignore
|
||||
piece[k] = change[k]
|
||||
|
|
@ -415,13 +278,12 @@ const getPieceBounds = (gameId: string, tileIdx: number): Rect => {
|
|||
}
|
||||
}
|
||||
|
||||
const getTileZIndex = (gameId: string, tileIdx: number): number => {
|
||||
const tile = getPiece(gameId, tileIdx)
|
||||
return tile.z
|
||||
const getPieceZIndex = (gameId: string, pieceIdx: number): number => {
|
||||
return getPiece(gameId, pieceIdx).z
|
||||
}
|
||||
|
||||
const getFirstOwnedPieceIdx = (gameId: string, playerId: string): number => {
|
||||
for (let t of GAMES[gameId].puzzle.tiles) {
|
||||
for (const t of GAMES[gameId].puzzle.tiles) {
|
||||
const tile = Util.decodePiece(t)
|
||||
if (tile.owner === playerId) {
|
||||
return tile.idx
|
||||
|
|
@ -430,7 +292,10 @@ const getFirstOwnedPieceIdx = (gameId: string, playerId: string): number => {
|
|||
return -1
|
||||
}
|
||||
|
||||
const getFirstOwnedPiece = (gameId: string, playerId: string): EncodedPiece|null => {
|
||||
const getFirstOwnedPiece = (
|
||||
gameId: string,
|
||||
playerId: string
|
||||
): EncodedPiece|null => {
|
||||
const idx = getFirstOwnedPieceIdx(gameId, playerId)
|
||||
return idx < 0 ? null : GAMES[gameId].puzzle.tiles[idx]
|
||||
}
|
||||
|
|
@ -463,12 +328,12 @@ const getMaxZIndex = (gameId: string): number => {
|
|||
return GAMES[gameId].puzzle.data.maxZ
|
||||
}
|
||||
|
||||
const getMaxZIndexByTileIdxs = (gameId: string, tileIdxs: Array<number>): number => {
|
||||
const getMaxZIndexByPieceIdxs = (gameId: string, pieceIdxs: Array<number>): number => {
|
||||
let maxZ = 0
|
||||
for (let tileIdx of tileIdxs) {
|
||||
let tileZIndex = getTileZIndex(gameId, tileIdx)
|
||||
if (tileZIndex > maxZ) {
|
||||
maxZ = tileZIndex
|
||||
for (const pieceIdx of pieceIdxs) {
|
||||
const curZ = getPieceZIndex(gameId, pieceIdx)
|
||||
if (curZ > maxZ) {
|
||||
maxZ = curZ
|
||||
}
|
||||
}
|
||||
return maxZ
|
||||
|
|
@ -477,7 +342,7 @@ const getMaxZIndexByTileIdxs = (gameId: string, tileIdxs: Array<number>): number
|
|||
function srcPosByTileIdx(gameId: string, tileIdx: number): Point {
|
||||
const info = GAMES[gameId].puzzle.info
|
||||
|
||||
const c = Util.coordByTileIdx(info, tileIdx)
|
||||
const c = Util.coordByPieceIdx(info, tileIdx)
|
||||
const cx = c.x * info.tileSize
|
||||
const cy = c.y * info.tileSize
|
||||
|
||||
|
|
@ -487,7 +352,7 @@ function srcPosByTileIdx(gameId: string, tileIdx: number): Point {
|
|||
function getSurroundingTilesByIdx(gameId: string, tileIdx: number) {
|
||||
const info = GAMES[gameId].puzzle.info
|
||||
|
||||
const c = Util.coordByTileIdx(info, tileIdx)
|
||||
const c = Util.coordByPieceIdx(info, tileIdx)
|
||||
|
||||
return [
|
||||
// top
|
||||
|
|
@ -501,29 +366,29 @@ function getSurroundingTilesByIdx(gameId: string, tileIdx: number) {
|
|||
]
|
||||
}
|
||||
|
||||
const setTilesZIndex = (gameId: string, tileIdxs: Array<number>, zIndex: number): void => {
|
||||
for (let tilesIdx of tileIdxs) {
|
||||
changeTile(gameId, tilesIdx, { z: zIndex })
|
||||
const setPiecesZIndex = (gameId: string, tileIdxs: Array<number>, zIndex: number): void => {
|
||||
for (const tilesIdx of tileIdxs) {
|
||||
changePiece(gameId, tilesIdx, { z: zIndex })
|
||||
}
|
||||
}
|
||||
|
||||
const moveTileDiff = (gameId: string, tileIdx: number, diff: Point): void => {
|
||||
const oldPos = getPiecePos(gameId, tileIdx)
|
||||
const pos = Geometry.pointAdd(oldPos, diff)
|
||||
changeTile(gameId, tileIdx, { pos })
|
||||
changePiece(gameId, tileIdx, { pos })
|
||||
}
|
||||
|
||||
const moveTilesDiff = (
|
||||
const movePiecesDiff = (
|
||||
gameId: string,
|
||||
tileIdxs: Array<number>,
|
||||
pieceIdxs: Array<number>,
|
||||
diff: Point
|
||||
): void => {
|
||||
const drawSize = getPieceDrawSize(gameId)
|
||||
const bounds = getBounds(gameId)
|
||||
const cappedDiff = diff
|
||||
|
||||
for (let tileIdx of tileIdxs) {
|
||||
const t = getPiece(gameId, tileIdx)
|
||||
for (const pieceIdx of pieceIdxs) {
|
||||
const t = getPiece(gameId, pieceIdx)
|
||||
if (t.pos.x + diff.x < bounds.x) {
|
||||
cappedDiff.x = Math.max(bounds.x - t.pos.x, cappedDiff.x)
|
||||
} else if (t.pos.x + drawSize + diff.x > bounds.x + bounds.w) {
|
||||
|
|
@ -536,24 +401,24 @@ const moveTilesDiff = (
|
|||
}
|
||||
}
|
||||
|
||||
for (let tileIdx of tileIdxs) {
|
||||
moveTileDiff(gameId, tileIdx, cappedDiff)
|
||||
for (const pieceIdx of pieceIdxs) {
|
||||
moveTileDiff(gameId, pieceIdx, cappedDiff)
|
||||
}
|
||||
}
|
||||
|
||||
const finishTiles = (gameId: string, tileIdxs: Array<number>): void => {
|
||||
for (let tileIdx of tileIdxs) {
|
||||
changeTile(gameId, tileIdx, { owner: -1, z: 1 })
|
||||
const finishPieces = (gameId: string, pieceIdxs: Array<number>): void => {
|
||||
for (const pieceIdx of pieceIdxs) {
|
||||
changePiece(gameId, pieceIdx, { owner: -1, z: 1 })
|
||||
}
|
||||
}
|
||||
|
||||
const setTilesOwner = (
|
||||
gameId: string,
|
||||
tileIdxs: Array<number>,
|
||||
pieceIdxs: Array<number>,
|
||||
owner: string|number
|
||||
): void => {
|
||||
for (let tileIdx of tileIdxs) {
|
||||
changeTile(gameId, tileIdx, { owner })
|
||||
for (const pieceIdx of pieceIdxs) {
|
||||
changePiece(gameId, pieceIdx, { owner })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -564,7 +429,7 @@ function getGroupedPieceIdxs(gameId: string, pieceIdx: number): number[] {
|
|||
|
||||
const grouped = []
|
||||
if (piece.group) {
|
||||
for (let other of pieces) {
|
||||
for (const other of pieces) {
|
||||
const otherPiece = Util.decodePiece(other)
|
||||
if (otherPiece.group === piece.group) {
|
||||
grouped.push(otherPiece.idx)
|
||||
|
|
@ -579,8 +444,8 @@ function getGroupedPieceIdxs(gameId: string, pieceIdx: number): number[] {
|
|||
// Returns the index of the puzzle tile with the highest z index
|
||||
// that is not finished yet and that matches the position
|
||||
const freePieceIdxByPos = (gameId: string, pos: Point): number => {
|
||||
let info = GAMES[gameId].puzzle.info
|
||||
let pieces = GAMES[gameId].puzzle.tiles
|
||||
const info = GAMES[gameId].puzzle.info
|
||||
const pieces = GAMES[gameId].puzzle.tiles
|
||||
|
||||
let maxZ = -1
|
||||
let pieceIdx = -1
|
||||
|
|
@ -664,28 +529,28 @@ const getPuzzleHeight = (gameId: string): number => {
|
|||
function handleInput(
|
||||
gameId: string,
|
||||
playerId: string,
|
||||
input: any,
|
||||
ts: number
|
||||
): Array<Array<any>> {
|
||||
input: Input,
|
||||
ts: Timestamp
|
||||
): Array<Change> {
|
||||
const puzzle = GAMES[gameId].puzzle
|
||||
const evtInfo = getEvtInfo(gameId, playerId)
|
||||
|
||||
const changes = [] as Array<Array<any>>
|
||||
const changes: Array<Change> = []
|
||||
|
||||
const _dataChange = (): void => {
|
||||
changes.push([Protocol.CHANGE_DATA, puzzle.data])
|
||||
}
|
||||
|
||||
const _tileChange = (tileIdx: number): void => {
|
||||
const _pieceChange = (pieceIdx: number): void => {
|
||||
changes.push([
|
||||
Protocol.CHANGE_TILE,
|
||||
Util.encodePiece(getPiece(gameId, tileIdx)),
|
||||
Util.encodePiece(getPiece(gameId, pieceIdx)),
|
||||
])
|
||||
}
|
||||
|
||||
const _tileChanges = (tileIdxs: Array<number>): void => {
|
||||
for (const tileIdx of tileIdxs) {
|
||||
_tileChange(tileIdx)
|
||||
const _pieceChanges = (pieceIdxs: Array<number>): void => {
|
||||
for (const pieceIdx of pieceIdxs) {
|
||||
_pieceChange(pieceIdx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -703,12 +568,12 @@ function handleInput(
|
|||
// put both tiles (and their grouped tiles) in the same group
|
||||
const groupTiles = (
|
||||
gameId: string,
|
||||
tileIdx1: number,
|
||||
tileIdx2: number
|
||||
pieceIdx1: number,
|
||||
pieceIdx2: number
|
||||
): void => {
|
||||
const tiles = GAMES[gameId].puzzle.tiles
|
||||
const group1 = getPieceGroup(gameId, tileIdx1)
|
||||
const group2 = getPieceGroup(gameId, tileIdx2)
|
||||
const pieces = GAMES[gameId].puzzle.tiles
|
||||
const group1 = getPieceGroup(gameId, pieceIdx1)
|
||||
const group2 = getPieceGroup(gameId, pieceIdx2)
|
||||
|
||||
let group
|
||||
const searchGroups = []
|
||||
|
|
@ -729,18 +594,18 @@ function handleInput(
|
|||
group = getMaxGroup(gameId)
|
||||
}
|
||||
|
||||
changeTile(gameId, tileIdx1, { group })
|
||||
_tileChange(tileIdx1)
|
||||
changeTile(gameId, tileIdx2, { group })
|
||||
_tileChange(tileIdx2)
|
||||
changePiece(gameId, pieceIdx1, { group })
|
||||
_pieceChange(pieceIdx1)
|
||||
changePiece(gameId, pieceIdx2, { group })
|
||||
_pieceChange(pieceIdx2)
|
||||
|
||||
// TODO: strange
|
||||
if (searchGroups.length > 0) {
|
||||
for (const t of tiles) {
|
||||
const piece = Util.decodePiece(t)
|
||||
for (const p of pieces) {
|
||||
const piece = Util.decodePiece(p)
|
||||
if (searchGroups.includes(piece.group)) {
|
||||
changeTile(gameId, piece.idx, { group })
|
||||
_tileChange(piece.idx)
|
||||
changePiece(gameId, piece.idx, { group })
|
||||
_pieceChange(piece.idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -770,13 +635,13 @@ function handleInput(
|
|||
|
||||
const tileIdxAtPos = freePieceIdxByPos(gameId, pos)
|
||||
if (tileIdxAtPos >= 0) {
|
||||
let maxZ = getMaxZIndex(gameId) + 1
|
||||
const maxZ = getMaxZIndex(gameId) + 1
|
||||
changeData(gameId, { maxZ })
|
||||
_dataChange()
|
||||
const tileIdxs = getGroupedPieceIdxs(gameId, tileIdxAtPos)
|
||||
setTilesZIndex(gameId, tileIdxs, getMaxZIndex(gameId))
|
||||
setPiecesZIndex(gameId, tileIdxs, getMaxZIndex(gameId))
|
||||
setTilesOwner(gameId, tileIdxs, playerId)
|
||||
_tileChanges(tileIdxs)
|
||||
_pieceChanges(tileIdxs)
|
||||
}
|
||||
evtInfo._last_mouse = pos
|
||||
|
||||
|
|
@ -790,18 +655,18 @@ function handleInput(
|
|||
changePlayer(gameId, playerId, {x, y, ts})
|
||||
_playerChange()
|
||||
} else {
|
||||
let tileIdx = getFirstOwnedPieceIdx(gameId, playerId)
|
||||
if (tileIdx >= 0) {
|
||||
const pieceIdx = getFirstOwnedPieceIdx(gameId, playerId)
|
||||
if (pieceIdx >= 0) {
|
||||
// player is moving a tile (and hand)
|
||||
changePlayer(gameId, playerId, {x, y, ts})
|
||||
_playerChange()
|
||||
|
||||
// check if pos is on the tile, otherwise dont move
|
||||
// (mouse could be out of table, but tile stays on it)
|
||||
const tileIdxs = getGroupedPieceIdxs(gameId, tileIdx)
|
||||
const pieceIdxs = getGroupedPieceIdxs(gameId, pieceIdx)
|
||||
let anyOk = Geometry.pointInBounds(pos, getBounds(gameId))
|
||||
&& Geometry.pointInBounds(evtInfo._last_mouse_down, getBounds(gameId))
|
||||
for (let idx of tileIdxs) {
|
||||
for (const idx of pieceIdxs) {
|
||||
const bounds = getPieceBounds(gameId, idx)
|
||||
if (Geometry.pointInBounds(pos, bounds)) {
|
||||
anyOk = true
|
||||
|
|
@ -813,9 +678,9 @@ function handleInput(
|
|||
const diffY = y - evtInfo._last_mouse_down.y
|
||||
|
||||
const diff = { x: diffX, y: diffY }
|
||||
moveTilesDiff(gameId, tileIdxs, diff)
|
||||
movePiecesDiff(gameId, pieceIdxs, diff)
|
||||
|
||||
_tileChanges(tileIdxs)
|
||||
_pieceChanges(pieceIdxs)
|
||||
}
|
||||
} else {
|
||||
// player is just moving map, so no change in position!
|
||||
|
|
@ -835,26 +700,26 @@ function handleInput(
|
|||
|
||||
evtInfo._last_mouse_down = null
|
||||
|
||||
let tileIdx = getFirstOwnedPieceIdx(gameId, playerId)
|
||||
if (tileIdx >= 0) {
|
||||
const pieceIdx = getFirstOwnedPieceIdx(gameId, playerId)
|
||||
if (pieceIdx >= 0) {
|
||||
// drop the tile(s)
|
||||
let tileIdxs = getGroupedPieceIdxs(gameId, tileIdx)
|
||||
setTilesOwner(gameId, tileIdxs, 0)
|
||||
_tileChanges(tileIdxs)
|
||||
const pieceIdxs = getGroupedPieceIdxs(gameId, pieceIdx)
|
||||
setTilesOwner(gameId, pieceIdxs, 0)
|
||||
_pieceChanges(pieceIdxs)
|
||||
|
||||
// Check if the tile was dropped near the final location
|
||||
let tilePos = getPiecePos(gameId, tileIdx)
|
||||
let finalPos = getFinalPiecePos(gameId, tileIdx)
|
||||
const tilePos = getPiecePos(gameId, pieceIdx)
|
||||
const finalPos = getFinalPiecePos(gameId, pieceIdx)
|
||||
if (Geometry.pointDistance(finalPos, tilePos) < puzzle.info.snapDistance) {
|
||||
let diff = Geometry.pointSub(finalPos, tilePos)
|
||||
const diff = Geometry.pointSub(finalPos, tilePos)
|
||||
// Snap the tile to the final destination
|
||||
moveTilesDiff(gameId, tileIdxs, diff)
|
||||
finishTiles(gameId, tileIdxs)
|
||||
_tileChanges(tileIdxs)
|
||||
movePiecesDiff(gameId, pieceIdxs, diff)
|
||||
finishPieces(gameId, pieceIdxs)
|
||||
_pieceChanges(pieceIdxs)
|
||||
|
||||
let points = getPlayerPoints(gameId, playerId)
|
||||
if (getScoreMode(gameId) === ScoreMode.FINAL) {
|
||||
points += tileIdxs.length
|
||||
points += pieceIdxs.length
|
||||
} else if (getScoreMode(gameId) === ScoreMode.ANY) {
|
||||
points += 1
|
||||
} else {
|
||||
|
|
@ -877,7 +742,7 @@ function handleInput(
|
|||
otherTileIdx: number,
|
||||
off: Array<number>
|
||||
): boolean => {
|
||||
let info = GAMES[gameId].puzzle.info
|
||||
const info = GAMES[gameId].puzzle.info
|
||||
if (otherTileIdx < 0) {
|
||||
return false
|
||||
}
|
||||
|
|
@ -890,27 +755,27 @@ function handleInput(
|
|||
{x: off[0] * info.tileSize, y: off[1] * info.tileSize}
|
||||
)
|
||||
if (Geometry.pointDistance(tilePos, dstPos) < info.snapDistance) {
|
||||
let diff = Geometry.pointSub(dstPos, tilePos)
|
||||
let tileIdxs = getGroupedPieceIdxs(gameId, tileIdx)
|
||||
moveTilesDiff(gameId, tileIdxs, diff)
|
||||
const diff = Geometry.pointSub(dstPos, tilePos)
|
||||
let pieceIdxs = getGroupedPieceIdxs(gameId, tileIdx)
|
||||
movePiecesDiff(gameId, pieceIdxs, diff)
|
||||
groupTiles(gameId, tileIdx, otherTileIdx)
|
||||
tileIdxs = getGroupedPieceIdxs(gameId, tileIdx)
|
||||
const zIndex = getMaxZIndexByTileIdxs(gameId, tileIdxs)
|
||||
setTilesZIndex(gameId, tileIdxs, zIndex)
|
||||
_tileChanges(tileIdxs)
|
||||
pieceIdxs = getGroupedPieceIdxs(gameId, tileIdx)
|
||||
const zIndex = getMaxZIndexByPieceIdxs(gameId, pieceIdxs)
|
||||
setPiecesZIndex(gameId, pieceIdxs, zIndex)
|
||||
_pieceChanges(pieceIdxs)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
let snapped = false
|
||||
for (let tileIdxTmp of getGroupedPieceIdxs(gameId, tileIdx)) {
|
||||
let othersIdxs = getSurroundingTilesByIdx(gameId, tileIdxTmp)
|
||||
for (const pieceIdxTmp of getGroupedPieceIdxs(gameId, pieceIdx)) {
|
||||
const othersIdxs = getSurroundingTilesByIdx(gameId, pieceIdxTmp)
|
||||
if (
|
||||
check(gameId, tileIdxTmp, othersIdxs[0], [0, 1]) // top
|
||||
|| check(gameId, tileIdxTmp, othersIdxs[1], [-1, 0]) // right
|
||||
|| check(gameId, tileIdxTmp, othersIdxs[2], [0, -1]) // bottom
|
||||
|| check(gameId, tileIdxTmp, othersIdxs[3], [1, 0]) // left
|
||||
check(gameId, pieceIdxTmp, othersIdxs[0], [0, 1]) // top
|
||||
|| check(gameId, pieceIdxTmp, othersIdxs[1], [-1, 0]) // right
|
||||
|| check(gameId, pieceIdxTmp, othersIdxs[2], [0, -1]) // bottom
|
||||
|| check(gameId, pieceIdxTmp, othersIdxs[3], [1, 0]) // left
|
||||
) {
|
||||
snapped = true
|
||||
break
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export class Rng {
|
|||
random (min: number, max: number): number {
|
||||
this.rand_high = ((this.rand_high << 16) + (this.rand_high >> 16) + this.rand_low) & 0xffffffff;
|
||||
this.rand_low = (this.rand_low + this.rand_high) & 0xffffffff;
|
||||
var n = (this.rand_high >>> 0) / 0xffffffff;
|
||||
const n = (this.rand_high >>> 0) / 0xffffffff;
|
||||
return (min + n * (max-min+1))|0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,199 @@
|
|||
import { Point } from "./Geometry"
|
||||
import { Rng, RngSerialized } from "./Rng"
|
||||
|
||||
// @see https://stackoverflow.com/a/59906630/392905
|
||||
type ArrayLengthMutationKeys = 'splice' | 'push' | 'pop' | 'shift' | 'unshift' | number
|
||||
type ArrayItems<T extends Array<any>> = T extends Array<infer TItems> ? TItems : never
|
||||
export type FixedLengthArray<T extends any[]> =
|
||||
Pick<T, Exclude<keyof T, ArrayLengthMutationKeys>>
|
||||
& { [Symbol.iterator]: () => IterableIterator< ArrayItems<T> > }
|
||||
|
||||
export type Timestamp = number
|
||||
|
||||
export type Input = any
|
||||
export type Change = Array<any>
|
||||
|
||||
export type GameEvent = Array<any>
|
||||
|
||||
export type ClientEvent = Array<any>
|
||||
|
||||
export type EncodedPlayer = FixedLengthArray<[
|
||||
string,
|
||||
number,
|
||||
number,
|
||||
0|1,
|
||||
string|null,
|
||||
string|null,
|
||||
string|null,
|
||||
number,
|
||||
Timestamp,
|
||||
]>
|
||||
|
||||
export type EncodedPiece = FixedLengthArray<[
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
string|number,
|
||||
number,
|
||||
]>
|
||||
|
||||
export type EncodedPieceShape = number
|
||||
|
||||
export type EncodedGame = FixedLengthArray<[
|
||||
string,
|
||||
string,
|
||||
RngSerialized,
|
||||
Puzzle,
|
||||
Array<EncodedPlayer>,
|
||||
Record<string, EvtInfo>,
|
||||
ScoreMode,
|
||||
]>
|
||||
|
||||
export interface ReplayData {
|
||||
log: any[],
|
||||
game: EncodedGame|null
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
id: number
|
||||
slug: string
|
||||
title: string
|
||||
}
|
||||
|
||||
interface GameRng {
|
||||
obj: Rng
|
||||
type?: string
|
||||
}
|
||||
|
||||
export interface Game {
|
||||
id: string
|
||||
players: Array<EncodedPlayer>
|
||||
puzzle: Puzzle
|
||||
evtInfos: Record<string, EvtInfo>
|
||||
scoreMode?: ScoreMode
|
||||
rng: GameRng
|
||||
}
|
||||
|
||||
export interface Image {
|
||||
id: number
|
||||
filename: string
|
||||
file: string
|
||||
url: string
|
||||
title: string
|
||||
tags: Array<Tag>
|
||||
created: number
|
||||
}
|
||||
|
||||
export interface GameSettings {
|
||||
tiles: number
|
||||
image: Image
|
||||
scoreMode: ScoreMode
|
||||
}
|
||||
|
||||
export interface Puzzle {
|
||||
tiles: Array<EncodedPiece>
|
||||
data: PuzzleData
|
||||
info: PuzzleInfo
|
||||
}
|
||||
|
||||
export interface PuzzleData {
|
||||
started: number
|
||||
finished: number
|
||||
maxGroup: number
|
||||
maxZ: number
|
||||
}
|
||||
|
||||
export interface PuzzleDataChange {
|
||||
started?: number
|
||||
finished?: number
|
||||
maxGroup?: number
|
||||
maxZ?: number
|
||||
}
|
||||
|
||||
interface PuzzleTable {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
enum PieceEdge {
|
||||
Flat = 0,
|
||||
Out = 1,
|
||||
In = -1,
|
||||
}
|
||||
export interface PieceShape {
|
||||
top: PieceEdge
|
||||
bottom: PieceEdge
|
||||
left: PieceEdge
|
||||
right: PieceEdge
|
||||
}
|
||||
|
||||
export interface Piece {
|
||||
owner: string|number
|
||||
idx: number
|
||||
pos: Point
|
||||
z: number
|
||||
group: number
|
||||
}
|
||||
|
||||
export interface PieceChange {
|
||||
owner?: string|number
|
||||
idx?: number
|
||||
pos?: Point
|
||||
z?: number
|
||||
group?: number
|
||||
}
|
||||
|
||||
export interface PuzzleInfo {
|
||||
table: PuzzleTable
|
||||
targetTiles: number,
|
||||
imageUrl: string
|
||||
|
||||
width: number
|
||||
height: number
|
||||
tileSize: number
|
||||
tileDrawSize: number
|
||||
tileMarginWidth: number
|
||||
tileDrawOffset: number
|
||||
snapDistance: number
|
||||
|
||||
tiles: number
|
||||
tilesX: number
|
||||
tilesY: number
|
||||
|
||||
shapes: Array<EncodedPieceShape>
|
||||
}
|
||||
|
||||
export interface Player {
|
||||
id: string
|
||||
x: number
|
||||
y: number
|
||||
d: 0|1
|
||||
name: string|null
|
||||
color: string|null
|
||||
bgcolor: string|null
|
||||
points: number
|
||||
ts: Timestamp
|
||||
}
|
||||
|
||||
export interface PlayerChange {
|
||||
id?: string
|
||||
x?: number
|
||||
y?: number
|
||||
d?: 0|1
|
||||
name?: string|null
|
||||
color?: string|null
|
||||
bgcolor?: string|null
|
||||
points?: number
|
||||
ts?: Timestamp
|
||||
}
|
||||
|
||||
export interface EvtInfo {
|
||||
_last_mouse: Point|null
|
||||
_last_mouse_down: Point|null
|
||||
}
|
||||
|
||||
export enum ScoreMode {
|
||||
FINAL = 0,
|
||||
ANY = 1,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { PuzzleCreationInfo } from '../server/Puzzle'
|
||||
import {
|
||||
EncodedGame,
|
||||
EncodedPiece,
|
||||
|
|
@ -7,19 +8,20 @@ import {
|
|||
Piece,
|
||||
PieceShape,
|
||||
Player,
|
||||
PuzzleInfo,
|
||||
ScoreMode
|
||||
} from './GameCommon'
|
||||
} from './Types'
|
||||
import { Point } from './Geometry'
|
||||
import { Rng } from './Rng'
|
||||
|
||||
const slug = (str: string) => {
|
||||
const slug = (str: string): string => {
|
||||
let tmp = str.toLowerCase()
|
||||
tmp = tmp.replace(/[^a-z0-9]+/g, '-')
|
||||
tmp = tmp.replace(/^-|-$/, '')
|
||||
return tmp
|
||||
}
|
||||
|
||||
const pad = (x: any, pad: string) => {
|
||||
const pad = (x: number, pad: string): string => {
|
||||
const str = `${x}`
|
||||
if (str.length >= pad.length) {
|
||||
return str
|
||||
|
|
@ -43,7 +45,9 @@ export const logger = (...pre: Array<any>) => {
|
|||
}
|
||||
|
||||
// get a unique id
|
||||
export const uniqId = () => Date.now().toString(36) + Math.random().toString(36).substring(2)
|
||||
export const uniqId = (): string => {
|
||||
return Date.now().toString(36) + Math.random().toString(36).substring(2)
|
||||
}
|
||||
|
||||
function encodeShape(data: PieceShape): EncodedPieceShape {
|
||||
/* encoded in 1 byte:
|
||||
|
|
@ -139,11 +143,11 @@ function decodeGame(data: EncodedGame): Game {
|
|||
}
|
||||
}
|
||||
|
||||
function coordByTileIdx(info: any, tileIdx: number): Point {
|
||||
function coordByPieceIdx(info: PuzzleInfo|PuzzleCreationInfo, pieceIdx: number): Point {
|
||||
const wTiles = info.width / info.tileSize
|
||||
return {
|
||||
x: tileIdx % wTiles,
|
||||
y: Math.floor(tileIdx / wTiles),
|
||||
x: pieceIdx % wTiles,
|
||||
y: Math.floor(pieceIdx / wTiles),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -151,16 +155,16 @@ const hash = (str: string): number => {
|
|||
let hash = 0
|
||||
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
let chr = str.charCodeAt(i);
|
||||
const chr = str.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + chr;
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
function asQueryArgs(data: any) {
|
||||
function asQueryArgs(data: Record<string, any>): string {
|
||||
const q = []
|
||||
for (let k in data) {
|
||||
for (const k in data) {
|
||||
const pair = [k, data[k]].map(encodeURIComponent)
|
||||
q.push(pair.join('='))
|
||||
}
|
||||
|
|
@ -187,7 +191,7 @@ export default {
|
|||
encodeGame,
|
||||
decodeGame,
|
||||
|
||||
coordByTileIdx,
|
||||
coordByPieceIdx,
|
||||
|
||||
asQueryArgs,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue