puzzle/server/Puzzle.js

208 lines
5.7 KiB
JavaScript
Raw Normal View History

2020-11-08 14:13:43 +01:00
import sizeOf from 'image-size'
2020-11-12 19:25:42 +01:00
import Util from './../common/Util.js'
2020-11-08 14:13:43 +01:00
// cut size of each puzzle tile in the
// final resized version of the puzzle image
const TILE_SIZE = 64
async function createPuzzle(targetTiles, image) {
2020-11-16 21:43:49 +01:00
const imagePath = './../game' + image
const imageUrl = image
2020-11-08 14:49:34 +01:00
2020-11-08 14:13:43 +01:00
// load bitmap, to determine the original size of the image
2020-11-16 21:43:49 +01:00
const dim = sizeOf(imagePath)
2020-11-08 14:13:43 +01:00
// determine puzzle information from the bitmap
2020-11-16 21:43:49 +01:00
const info = determinePuzzleInfo(dim.width, dim.height, targetTiles)
2020-11-08 14:13:43 +01:00
let tiles = new Array(info.tiles)
for (let i = 0; i < tiles.length; i++) {
2020-11-16 21:43:49 +01:00
tiles[i] = { idx: i }
2020-11-08 14:13:43 +01:00
}
const shapes = determinePuzzleTileShapes(info)
2020-11-08 16:42:59 +01:00
let positions = new Array(info.tiles)
for (let tile of tiles) {
positions[tile.idx] ={
// instead of info.tileSize, we use info.tileDrawSize
// to spread the tiles a bit
x: (info.coords[tile.idx].x) * (info.tileSize * 1.5),
y: (info.coords[tile.idx].y) * (info.tileSize * 1.5),
}
}
2020-11-16 21:43:49 +01:00
const tableWidth = info.width * 3
const tableHeight = info.height * 3
2020-11-08 16:42:59 +01:00
2020-11-16 21:43:49 +01:00
const off = info.tileSize * 1.5
let last = {
x: info.width - (1 * off),
y: info.height - (2 * off),
}
let countX = Math.ceil(info.width / off) + 2
let countY = Math.ceil(info.height / off) + 2
2020-11-08 16:42:59 +01:00
2020-11-16 21:43:49 +01:00
let diffX = off
let diffY = 0
2020-11-08 16:42:59 +01:00
let index = 0
for (let pos of positions) {
pos.x = last.x
pos.y = last.y
2020-11-16 21:43:49 +01:00
last.x+=diffX
last.y+=diffY
2020-11-08 16:42:59 +01:00
index++
// did we move horizontally?
2020-11-16 21:43:49 +01:00
if (diffX !== 0) {
if (index === countX) {
diffY = diffX
countY++
diffX = 0
2020-11-08 16:42:59 +01:00
index = 0
}
} else {
2020-11-16 21:43:49 +01:00
if (index === countY) {
diffX = -diffY
countX++
diffY = 0
2020-11-08 16:42:59 +01:00
index = 0
}
}
}
// then shuffle the positions
2020-11-12 19:25:42 +01:00
positions = Util.shuffle(positions)
2020-11-08 16:42:59 +01:00
tiles = tiles.map(tile => {
return {
idx: tile.idx, // index of tile in the array
group: 0, // if grouped with other tiles
z: 0, // z index of the tile
// who owns the tile
// 0 = free for taking
// -1 = finished
// other values: id of player who has the tile
owner: 0,
// physical current position of the tile (x/y in pixels)
// this position is the initial position only and is the
// value that changes when moving a tile
pos: positions[tile.idx],
}
})
2020-11-08 14:13:43 +01:00
// Complete puzzle object
2020-11-16 21:43:49 +01:00
return {
2020-11-08 14:13:43 +01:00
// tiles array
2020-11-08 16:42:59 +01:00
tiles,
2020-11-08 14:13:43 +01:00
// game data for puzzle, data changes during the game
data: {
// TODO: maybe calculate this each time?
maxZ: 0, // max z of all pieces
maxGroup: 0, // max group of all pieces
},
// static puzzle information. stays same for complete duration of
// the game
info: {
2020-11-08 16:42:59 +01:00
table: {
width: tableWidth,
height: tableHeight,
},
2020-11-08 14:13:43 +01:00
// information that was used to create the puzzle
targetTiles: targetTiles,
2020-11-16 21:43:49 +01:00
imageUrl,
2020-11-08 14:13:43 +01:00
width: info.width, // actual puzzle width (same as bitmap.width)
height: info.height, // actual puzzle height (same as bitmap.height)
tileSize: info.tileSize, // width/height of each tile (without tabs)
tileDrawSize: info.tileDrawSize, // width/height of each tile (with tabs)
tileMarginWidth: info.tileMarginWidth,
// offset in x and y when drawing tiles, so that they appear to be at pos
tileDrawOffset: (info.tileDrawSize - info.tileSize) / -2,
// max distance between tile and destination that
// makes the tile snap to destination
snapDistance: info.tileSize / 2,
tiles: info.tiles, // the final number of tiles in the puzzle
2020-11-16 21:43:49 +01:00
tilesX: info.tilesX, // number of tiles each row
tilesY: info.tilesY, // number of tiles each col
2020-11-08 14:13:43 +01:00
coords: info.coords, // map of tile index to its coordinates
// ( index => {x, y} )
// this is not the physical coordinate, but
// the tile_coordinate
// this can be used to determine where the
// final destination of a tile is
shapes: shapes, // tile shapes
},
}
}
function determinePuzzleTileShapes(info) {
const tabs = [-1, 1]
const shapes = new Array(info.tiles)
for (let i = 0; i < info.tiles; i++) {
shapes[i] = {
2020-11-16 21:43:49 +01:00
top: info.coords[i].y === 0 ? 0 : shapes[i - info.tilesX].bottom * -1,
right: info.coords[i].x === info.tilesX - 1 ? 0 : Util.choice(tabs),
2020-11-08 14:13:43 +01:00
left: info.coords[i].x === 0 ? 0 : shapes[i - 1].right * -1,
2020-11-16 21:43:49 +01:00
bottom: info.coords[i].y === info.tilesY - 1 ? 0 : Util.choice(tabs),
2020-11-08 14:13:43 +01:00
}
}
return shapes
}
2020-11-16 21:43:49 +01:00
const determineTilesXY = (w, h, targetTiles) => {
const w_ = w < h ? (w * h) : (w * w)
const h_ = w < h ? (h * h) : (w * h)
let size = 0
2020-11-08 14:13:43 +01:00
let tiles = 0
do {
2020-11-16 21:43:49 +01:00
size++
tiles = Math.floor(w_ / size) * Math.floor(h_ / size)
2020-11-08 14:13:43 +01:00
} while (tiles >= targetTiles)
2020-11-16 21:43:49 +01:00
size--
return {
tilesX: Math.round(w_ / size),
tilesY: Math.round(h_ / size),
}
}
2020-11-08 14:13:43 +01:00
2020-11-16 21:43:49 +01:00
const determinePuzzleInfo = (w, h, targetTiles) => {
const {tilesX, tilesY} = determineTilesXY(w, h, targetTiles)
const tiles = tilesX * tilesY
const tileSize = TILE_SIZE
const width = tilesX * tileSize
const height = tilesY * tileSize
const coords = buildCoords({ width, height, tileSize, tiles })
2020-11-08 14:13:43 +01:00
const tileMarginWidth = tileSize * .5;
const tileDrawSize = Math.round(tileSize + tileMarginWidth * 2)
return {
width,
height,
tileSize,
tileMarginWidth,
tileDrawSize,
tiles,
2020-11-16 21:43:49 +01:00
tilesX,
tilesY,
2020-11-08 14:13:43 +01:00
coords,
}
}
2020-11-16 21:43:49 +01:00
const buildCoords = (puzzleInfo) => {
const wTiles = puzzleInfo.width / puzzleInfo.tileSize
2020-11-08 14:13:43 +01:00
const coords = new Array(puzzleInfo.tiles)
for (let i = 0; i < puzzleInfo.tiles; i++) {
2020-11-16 21:43:49 +01:00
const y = Math.floor(i / wTiles)
const x = i % wTiles
2020-11-08 14:13:43 +01:00
coords[i] = { x, y }
}
return coords
}
export {
2020-11-12 19:19:02 +01:00
createPuzzle,
2020-11-08 14:13:43 +01:00
}