add sorting game

This commit is contained in:
lagaffe 2026-04-22 18:48:13 +02:00
parent 8f08f96096
commit 874c5591d5
9 changed files with 138 additions and 6 deletions

View file

@ -8,6 +8,7 @@ run rundev.sh, the site will be available at http://localhost:5173/ and you shou
#### to deploy
run run.sh, it build the production image and starts a container
#### notes
I tried to make a clean setup, so 2 docker files, one for quick tests during dev the other for production. It is not possible to merge the two dockerfiles because the dev one needs a bind volume to update code from host which we dont want in production

BIN
src/assets/img/arduino.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
src/assets/img/shelf.avif Normal file

Binary file not shown.

BIN
src/assets/img/trashcan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 KiB

View file

@ -1,6 +1,7 @@
function onDragMove(node, event, shift = { x: 0, y: 0 }) {
// shift is a tuple representing the shift between the coords of the node and the pointer
const dragPoint = event.global;
dragPoint.x -= shift.x;
dragPoint.y -= shift.y;
const parent = node.parent;
@ -32,7 +33,7 @@ function onDragMove(node, event, shift = { x: 0, y: 0 }) {
function onDragEnd(node, onDrop) {
node.off("pointerup");
node.off("pointerupoutside");
node.parent.off("mousemove");
node.off("globalpointermove");
onDrop(node);
}
@ -42,10 +43,10 @@ function onDragStart(node, onDrop, event) {
node.on("pointerupoutside", () => onDragEnd(node, onDrop));
node.parent.eventMode =
node.parent.eventMode == "dynamic" ? "dynamic" : "static";
const dragShift = node.parent.toLocal(event.global, null);
const dragShift = node.parent.toLocal(event.global.clone(), null);
dragShift.x -= node.x;
dragShift.y -= node.y;
node.parent.on("mousemove", (event) => {
node.on("globalpointermove", (event) => {
onDragMove(node, event, dragShift);
});
}

111
src/games/cleanupHS.js Normal file
View file

@ -0,0 +1,111 @@
import { Application, Assets, Container, Sprite, Rectangle } from "pixi.js";
import { Game } from "./game";
import { KeyBoardListener, makeDragable, getIntersection } from "../common";
const itemTypes = { TRASH: "TRASH", HARDWARE: "HARDWARE" };
export class CleanupHS extends Game {
constructor(width, height) {
super();
this._elementCount = this._difficulty * 6;
this._elementsPlaced = 0;
this.gameContainer.layout = {
width: "100%",
height: "100%",
justifyContent: "space-between",
};
const trashcan = Sprite.from("trashcan");
const shelf = Sprite.from("shelf");
this.trashLandingZone = new Container();
this.shelfLandingZone = new Container();
this.trashLandingZone.addChild(trashcan);
this.shelfLandingZone.addChild(shelf);
this.trashLandingZone.layout = { width: "20%", height: "100%" };
this.shelfLandingZone.layout = {
width: "20%",
height: "100%",
};
trashcan.layout = { objectFit: "contain" };
shelf.layout = { objectFit: "contain" };
this._items = [];
for (let i = 0; i < this._elementCount; i++) {
const item = {};
if (Math.random() > 0.5) {
item.type = itemTypes.TRASH;
item.sprite = Sprite.from("trash");
} else {
item.type = itemTypes.HARDWARE;
item.sprite = Sprite.from("arduino");
}
this._items.push(item);
// non dynamic sizes for now
const w = width * 0.04;
item.sprite.width = w;
item.sprite.height = w;
makeDragable(item.sprite, (node) => {
this._onItemDrop(item);
});
this.gameContainer.addChild(item.sprite);
}
this.gameContainer.addChild(this.trashLandingZone);
this.gameContainer.addChild(this.shelfLandingZone);
}
reset() {}
_onItemDrop(item) {
console.log("dropped", item.type);
const intersection = new Rectangle();
const overlapGreaterThan = (threshold, rect1, rect2) => {
getIntersection(rect1, rect2, intersection);
const intersectArea = intersection.width * intersection.height;
const rect1Area = rect1.width * rect1.height;
return intersectArea > threshold * rect1Area;
};
if (
overlapGreaterThan(
0.6,
item.sprite.getBounds(),
this.trashLandingZone.getBounds(),
)
) {
if (item.type != itemTypes.TRASH) {
this._onError();
}
this._elementsPlaced++;
this.gameContainer.removeChild(item.sprite);
} else if (
overlapGreaterThan(
0.6,
item.sprite.getBounds(),
this.shelfLandingZone.getBounds(),
)
) {
if (item.type != itemTypes.HARDWARE) {
this._onError();
}
this._elementsPlaced++;
this.gameContainer.removeChild(item.sprite);
}
if (this._elementsPlaced >= this._elementCount) {
this._win();
}
}
_onError() {
console.log("wrong placement");
// apply penalty for putting item in the wrong place
//probably a time loss
}
}

View file

@ -1,3 +1,4 @@
export { Fridge } from "./fridge.js";
export { Opinator } from "./opinator.js";
export { SmartMonday } from "./smartMonday.js";
export { CleanupHS } from "./cleanupHS.js";

View file

@ -1,6 +1,6 @@
import "@pixi/layout";
import { Application, Assets, Container, Sprite } from "pixi.js";
import { Fridge, Opinator, SmartMonday } from "./games";
import { Fridge, Opinator, SmartMonday, CleanupHS } from "./games";
import { backgroundLayout, KeyBoardListener } from "./common";
const app = new Application();
@ -51,6 +51,22 @@ async function preload() {
alias: "mate_caisse",
src: "assets/img/caisse.jpg",
},
{
alias: "trash",
src: "assets/img/empty_monster.jpg",
},
{
alias: "trashcan",
src: "assets/img/trashcan.png",
},
{
alias: "arduino",
src: "assets/img/arduino.jpg",
},
{
alias: "shelf",
src: "assets/img/shelf.avif",
},
];
await Assets.load(assets);
@ -83,9 +99,11 @@ function switchToGame(gameContainer, newGame) {
const opinator = new Opinator();
const fridge = new Fridge();
const smartMonday = new SmartMonday();
const cleanupHS = new CleanupHS(root.width, root.height);
//gameContainter.addChild(fridge.gameContainer);
gameContainter.addChild(opinator.gameContainer);
gameContainter.addChild(smartMonday.gameContainer);
//gameContainter.addChild(opinator.gameContainer);
//gameContainter.addChild(smartMonday.gameContainer);
gameContainter.addChild(cleanupHS.gameContainer);
root.addChild(gameContainter);
})();