add shape modes any and flat

This commit is contained in:
Zutatensuppe 2021-06-03 09:07:57 +02:00
parent b88321bb1b
commit 2d83fd441f
9 changed files with 98 additions and 30 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>🧩 jigsaw.hyottoko.club</title> <title>🧩 jigsaw.hyottoko.club</title>
<script type="module" crossorigin src="/assets/index.3ca85e0f.js"></script> <script type="module" crossorigin src="/assets/index.3208e2e9.js"></script>
<link rel="modulepreload" href="/assets/vendor.18cd2d7e.js"> <link rel="modulepreload" href="/assets/vendor.18cd2d7e.js">
<link rel="stylesheet" href="/assets/index.f7304069.css"> <link rel="stylesheet" href="/assets/index.f7304069.css">
</head> </head>

View file

@ -24,6 +24,12 @@ var ScoreMode;
ScoreMode[ScoreMode["FINAL"] = 0] = "FINAL"; ScoreMode[ScoreMode["FINAL"] = 0] = "FINAL";
ScoreMode[ScoreMode["ANY"] = 1] = "ANY"; ScoreMode[ScoreMode["ANY"] = 1] = "ANY";
})(ScoreMode || (ScoreMode = {})); })(ScoreMode || (ScoreMode = {}));
var ShapeMode;
(function (ShapeMode) {
ShapeMode[ShapeMode["NORMAL"] = 0] = "NORMAL";
ShapeMode[ShapeMode["ANY"] = 1] = "ANY";
ShapeMode[ShapeMode["FLAT"] = 2] = "FLAT";
})(ShapeMode || (ShapeMode = {}));
class Rng { class Rng {
constructor(seed) { constructor(seed) {
@ -1433,7 +1439,7 @@ var Images = {
// cut size of each puzzle tile in the // cut size of each puzzle tile in the
// final resized version of the puzzle image // final resized version of the puzzle image
const TILE_SIZE = 64; const TILE_SIZE = 64;
async function createPuzzle(rng, targetTiles, image, ts) { async function createPuzzle(rng, targetTiles, image, ts, shapeMode) {
const imagePath = image.file; const imagePath = image.file;
const imageUrl = image.url; const imageUrl = image.url;
// determine puzzle information from the image dimensions // determine puzzle information from the image dimensions
@ -1446,7 +1452,7 @@ async function createPuzzle(rng, targetTiles, image, ts) {
for (let i = 0; i < rawPieces.length; i++) { for (let i = 0; i < rawPieces.length; i++) {
rawPieces[i] = { idx: i }; rawPieces[i] = { idx: i };
} }
const shapes = determinePuzzleTileShapes(rng, info); const shapes = determinePuzzleTileShapes(rng, info, shapeMode);
let positions = new Array(info.tiles); let positions = new Array(info.tiles);
for (const piece of rawPieces) { for (const piece of rawPieces) {
const coord = Util.coordByPieceIdx(info, piece.idx); const coord = Util.coordByPieceIdx(info, piece.idx);
@ -1555,8 +1561,19 @@ async function createPuzzle(rng, targetTiles, image, ts) {
}, },
}; };
} }
function determinePuzzleTileShapes(rng, info) { function determineTabs(shapeMode) {
const tabs = [-1, 1]; switch (shapeMode) {
case ShapeMode.ANY:
return [-1, 0, 1];
case ShapeMode.FLAT:
return [0];
case ShapeMode.NORMAL:
default:
return [-1, 1];
}
}
function determinePuzzleTileShapes(rng, info, shapeMode) {
const tabs = determineTabs(shapeMode);
const shapes = new Array(info.tiles); const shapes = new Array(info.tiles);
for (let i = 0; i < info.tiles; i++) { for (let i = 0; i < info.tiles; i++) {
const coord = Util.coordByPieceIdx(info, i); const coord = Util.coordByPieceIdx(info, i);
@ -1692,22 +1709,23 @@ var GameStorage = {
setDirty, setDirty,
}; };
async function createGameObject(gameId, targetTiles, image, ts, scoreMode) { async function createGameObject(gameId, targetTiles, image, ts, scoreMode, shapeMode) {
const seed = Util.hash(gameId + ' ' + ts); const seed = Util.hash(gameId + ' ' + ts);
const rng = new Rng(seed); const rng = new Rng(seed);
return { return {
id: gameId, id: gameId,
rng: { type: 'Rng', obj: rng }, rng: { type: 'Rng', obj: rng },
puzzle: await createPuzzle(rng, targetTiles, image, ts), puzzle: await createPuzzle(rng, targetTiles, image, ts, shapeMode),
players: [], players: [],
evtInfos: {}, evtInfos: {},
scoreMode, scoreMode,
shapeMode,
}; };
} }
async function createGame(gameId, targetTiles, image, ts, scoreMode) { async function createGame(gameId, targetTiles, image, ts, scoreMode, shapeMode) {
const gameObject = await createGameObject(gameId, targetTiles, image, ts, scoreMode); const gameObject = await createGameObject(gameId, targetTiles, image, ts, scoreMode, shapeMode);
GameLog.create(gameId); GameLog.create(gameId);
GameLog.log(gameId, Protocol.LOG_HEADER, 1, targetTiles, image, ts, scoreMode); GameLog.log(gameId, Protocol.LOG_HEADER, 1, targetTiles, image, ts, scoreMode, shapeMode);
GameCommon.setGame(gameObject.id, gameObject); GameCommon.setGame(gameObject.id, gameObject);
GameStorage.setDirty(gameId); GameStorage.setDirty(gameId);
} }
@ -1981,7 +1999,7 @@ app.get('/api/replay-data', async (req, res) => {
let game = null; let game = null;
if (offset === 0) { if (offset === 0) {
// also need the game // also need the game
game = await Game.createGameObject(gameId, log[0][2], log[0][3], log[0][4], log[0][5] || ScoreMode.FINAL); game = await Game.createGameObject(gameId, log[0][2], log[0][3], log[0][4], log[0][5] || ScoreMode.FINAL, log[0][6] || ShapeMode.NORMAL);
} }
res.send({ log, game: game ? Util.encodeGame(game) : null }); res.send({ log, game: game ? Util.encodeGame(game) : null });
}); });
@ -2069,7 +2087,7 @@ app.post('/api/newgame', express.json(), async (req, res) => {
const gameId = Util.uniqId(); const gameId = Util.uniqId();
if (!GameCommon.exists(gameId)) { if (!GameCommon.exists(gameId)) {
const ts = Time.timestamp(); const ts = Time.timestamp();
await Game.createGame(gameId, gameSettings.tiles, gameSettings.image, ts, gameSettings.scoreMode); await Game.createGame(gameId, gameSettings.tiles, gameSettings.image, ts, gameSettings.scoreMode, gameSettings.shapeMode);
} }
res.send({ id: gameId }); res.send({ id: gameId });
}); });

View file

@ -73,6 +73,7 @@ export interface Game {
puzzle: Puzzle puzzle: Puzzle
evtInfos: Record<string, EvtInfo> evtInfos: Record<string, EvtInfo>
scoreMode?: ScoreMode scoreMode?: ScoreMode
shapeMode?: ShapeMode
rng: GameRng rng: GameRng
} }
@ -90,6 +91,7 @@ export interface GameSettings {
tiles: number tiles: number
image: Image image: Image
scoreMode: ScoreMode scoreMode: ScoreMode
shapeMode: ShapeMode
} }
export interface Puzzle { export interface Puzzle {
@ -198,3 +200,9 @@ export enum ScoreMode {
FINAL = 0, FINAL = 0,
ANY = 1, ANY = 1,
} }
export enum ShapeMode {
NORMAL = 0,
ANY = 1,
FLAT = 2,
}

View file

@ -22,6 +22,16 @@
<label><input type="radio" v-model="scoreMode" value="0" /> Final (Score when pieces are put to their final location)</label> <label><input type="radio" v-model="scoreMode" value="0" /> Final (Score when pieces are put to their final location)</label>
</td> </td>
</tr> </tr>
<tr>
<td><label>shapes: </label></td>
<td>
<label><input type="radio" v-model="shapeMode" value="0" /> Normal</label>
<br />
<label><input type="radio" v-model="shapeMode" value="1" /> Any (flat pieces can occur anywhere)</label>
<br />
<label><input type="radio" v-model="shapeMode" value="2" /> Flat (all pieces flat on all sides)</label>
</td>
</tr>
</table> </table>
</div> </div>
@ -36,7 +46,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { defineComponent } from 'vue'
import { GameSettings, ScoreMode } from './../../common/Types' import { GameSettings, ScoreMode, ShapeMode } from './../../common/Types'
import ResponsiveImage from './ResponsiveImage.vue' import ResponsiveImage from './ResponsiveImage.vue'
export default defineComponent({ export default defineComponent({
@ -58,6 +68,7 @@ export default defineComponent({
return { return {
tiles: 1000, tiles: 1000,
scoreMode: ScoreMode.ANY, scoreMode: ScoreMode.ANY,
shapeMode: ShapeMode.NORMAL,
} }
}, },
methods: { methods: {
@ -66,6 +77,7 @@ export default defineComponent({
tiles: this.tilesInt, tiles: this.tilesInt,
image: this.image, image: this.image,
scoreMode: this.scoreModeInt, scoreMode: this.scoreModeInt,
shapeMode: this.shapeModeInt,
} as GameSettings) } as GameSettings)
}, },
}, },
@ -84,6 +96,9 @@ export default defineComponent({
scoreModeInt (): number { scoreModeInt (): number {
return parseInt(`${this.scoreMode}`, 10) return parseInt(`${this.scoreMode}`, 10)
}, },
shapeModeInt (): number {
return parseInt(`${this.shapeMode}`, 10)
},
tilesInt (): number { tilesInt (): number {
return parseInt(`${this.tiles}`, 10) return parseInt(`${this.tiles}`, 10)
}, },

View file

@ -1,5 +1,5 @@
import GameCommon from './../common/GameCommon' import GameCommon from './../common/GameCommon'
import { Change, Game, Input, ScoreMode, Timestamp } from './../common/Types' import { Change, Game, Input, ScoreMode, ShapeMode, Timestamp } from './../common/Types'
import Util, { logger } from './../common/Util' import Util, { logger } from './../common/Util'
import { Rng } from './../common/Rng' import { Rng } from './../common/Rng'
import GameLog from './GameLog' import GameLog from './GameLog'
@ -14,17 +14,19 @@ async function createGameObject(
targetTiles: number, targetTiles: number,
image: PuzzleCreationImageInfo, image: PuzzleCreationImageInfo,
ts: Timestamp, ts: Timestamp,
scoreMode: ScoreMode scoreMode: ScoreMode,
shapeMode: ShapeMode
): Promise<Game> { ): Promise<Game> {
const seed = Util.hash(gameId + ' ' + ts) const seed = Util.hash(gameId + ' ' + ts)
const rng = new Rng(seed) const rng = new Rng(seed)
return { return {
id: gameId, id: gameId,
rng: { type: 'Rng', obj: rng }, rng: { type: 'Rng', obj: rng },
puzzle: await createPuzzle(rng, targetTiles, image, ts), puzzle: await createPuzzle(rng, targetTiles, image, ts, shapeMode),
players: [], players: [],
evtInfos: {}, evtInfos: {},
scoreMode, scoreMode,
shapeMode,
} }
} }
@ -33,18 +35,29 @@ async function createGame(
targetTiles: number, targetTiles: number,
image: PuzzleCreationImageInfo, image: PuzzleCreationImageInfo,
ts: Timestamp, ts: Timestamp,
scoreMode: ScoreMode scoreMode: ScoreMode,
shapeMode: ShapeMode
): Promise<void> { ): Promise<void> {
const gameObject = await createGameObject( const gameObject = await createGameObject(
gameId, gameId,
targetTiles, targetTiles,
image, image,
ts, ts,
scoreMode scoreMode,
shapeMode
) )
GameLog.create(gameId) GameLog.create(gameId)
GameLog.log(gameId, Protocol.LOG_HEADER, 1, targetTiles, image, ts, scoreMode) GameLog.log(
gameId,
Protocol.LOG_HEADER,
1,
targetTiles,
image,
ts,
scoreMode,
shapeMode
)
GameCommon.setGame(gameObject.id, gameObject) GameCommon.setGame(gameObject.id, gameObject)
GameStorage.setDirty(gameId) GameStorage.setDirty(gameId)

View file

@ -1,7 +1,7 @@
import Util from './../common/Util' import Util from './../common/Util'
import { Rng } from './../common/Rng' import { Rng } from './../common/Rng'
import Images from './Images' import Images from './Images'
import { EncodedPiece, EncodedPieceShape, PieceShape, Puzzle } from '../common/Types' import { EncodedPiece, EncodedPieceShape, PieceShape, Puzzle, ShapeMode } from '../common/Types'
import { Dim, Point } from '../common/Geometry' import { Dim, Point } from '../common/Geometry'
export interface PuzzleCreationImageInfo { export interface PuzzleCreationImageInfo {
@ -28,7 +28,8 @@ async function createPuzzle(
rng: Rng, rng: Rng,
targetTiles: number, targetTiles: number,
image: PuzzleCreationImageInfo, image: PuzzleCreationImageInfo,
ts: number ts: number,
shapeMode: ShapeMode
): Promise<Puzzle> { ): Promise<Puzzle> {
const imagePath = image.file const imagePath = image.file
const imageUrl = image.url const imageUrl = image.url
@ -44,7 +45,7 @@ async function createPuzzle(
for (let i = 0; i < rawPieces.length; i++) { for (let i = 0; i < rawPieces.length; i++) {
rawPieces[i] = { idx: i } rawPieces[i] = { idx: i }
} }
const shapes = determinePuzzleTileShapes(rng, info) const shapes = determinePuzzleTileShapes(rng, info, shapeMode)
let positions: Point[] = new Array(info.tiles) let positions: Point[] = new Array(info.tiles)
for (const piece of rawPieces) { for (const piece of rawPieces) {
@ -162,13 +163,24 @@ async function createPuzzle(
}, },
} }
} }
function determineTabs (shapeMode: ShapeMode): number[] {
switch(shapeMode) {
case ShapeMode.ANY:
return [-1, 0, 1]
case ShapeMode.FLAT:
return [0]
case ShapeMode.NORMAL:
default:
return [-1, 1]
}
}
function determinePuzzleTileShapes( function determinePuzzleTileShapes(
rng: Rng, rng: Rng,
info: PuzzleCreationInfo info: PuzzleCreationInfo,
shapeMode: ShapeMode
): Array<EncodedPieceShape> { ): Array<EncodedPieceShape> {
const tabs = [-1, 1] const tabs: number[] = determineTabs(shapeMode)
const shapes: Array<PieceShape> = new Array(info.tiles) const shapes: Array<PieceShape> = new Array(info.tiles)
for (let i = 0; i < info.tiles; i++) { for (let i = 0; i < info.tiles; i++) {
const coord = Util.coordByPieceIdx(info, i) const coord = Util.coordByPieceIdx(info, i)

View file

@ -19,7 +19,7 @@ import {
UPLOAD_DIR, UPLOAD_DIR,
} from './Dirs' } from './Dirs'
import GameCommon from '../common/GameCommon' import GameCommon from '../common/GameCommon'
import { ServerEvent, Game as GameType, GameSettings, ScoreMode } from '../common/Types' import { ServerEvent, Game as GameType, GameSettings, ScoreMode, ShapeMode } from '../common/Types'
import GameStorage from './GameStorage' import GameStorage from './GameStorage'
import Db from './Db' import Db from './Db'
@ -89,7 +89,8 @@ app.get('/api/replay-data', async (req, res): Promise<void> => {
log[0][2], log[0][2],
log[0][3], log[0][3],
log[0][4], log[0][4],
log[0][5] || ScoreMode.FINAL log[0][5] || ScoreMode.FINAL,
log[0][6] || ShapeMode.NORMAL
) )
} }
res.send({ log, game: game ? Util.encodeGame(game) : null }) res.send({ log, game: game ? Util.encodeGame(game) : null })
@ -201,7 +202,8 @@ app.post('/api/newgame', express.json(), async (req, res): Promise<void> => {
gameSettings.tiles, gameSettings.tiles,
gameSettings.image, gameSettings.image,
ts, ts,
gameSettings.scoreMode gameSettings.scoreMode,
gameSettings.shapeMode
) )
} }
res.send({ id: gameId }) res.send({ id: gameId })