rotation!

This commit is contained in:
Olivier Gagnon 2021-10-16 17:12:04 -04:00
parent c0420d1787
commit 092d5146b4
8 changed files with 94 additions and 50 deletions

@ -7,12 +7,14 @@ const noCharge = [FragmentType.None, FragmentType.Delete, FragmentType.Booster];
export interface IActiveFragmentParams {
x: number;
y: number;
rotation: number;
fragment: Fragment;
}
export class ActiveFragment {
id: number;
charge: number;
rotation: number;
x: number;
y: number;
@ -21,13 +23,14 @@ export class ActiveFragment {
this.id = params.fragment.id;
this.x = params.x;
this.y = params.y;
this.charge = 1;
if (noCharge.includes(params.fragment.type)) this.charge = 0;
this.charge = 0;
this.rotation = params.rotation;
} else {
this.id = -1;
this.x = -1;
this.y = -1;
this.charge = -1;
this.rotation = -1;
}
}
@ -39,7 +42,8 @@ export class ActiveFragment {
const dy: number = other.y - this.y;
for (let j = 0; j < thisFragment.shape.length; j++) {
for (let i = 0; i < thisFragment.shape[j].length; i++) {
if (thisFragment.fullAt(i, j) && otherFragment.fullAt(i - dx, j - dy)) return true;
if (thisFragment.fullAt(i, j, this.rotation) && otherFragment.fullAt(i - dx, j - dy, other.rotation))
return true;
}
}
@ -53,12 +57,12 @@ export class ActiveFragment {
}
fullAt(worldX: number, worldY: number): boolean {
return this.fragment().fullAt(worldX - this.x, worldY - this.y);
return this.fragment().fullAt(worldX - this.x, worldY - this.y, this.rotation);
}
neighboors(): number[][] {
return this.fragment()
.neighboors()
.neighboors(this.rotation)
.map((cell) => [this.x + cell[0], this.y + cell[1]]);
}
@ -66,7 +70,7 @@ export class ActiveFragment {
// We have to do a round trip because the constructor.
const fragment = FragmentById(this.id);
if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment.");
const c = new ActiveFragment({ x: this.x, y: this.y, fragment: fragment });
const c = new ActiveFragment({ x: this.x, y: this.y, rotation: this.rotation, fragment: fragment });
c.charge = this.charge;
return c;
}

@ -17,30 +17,44 @@ export class Fragment {
this.limit = limit;
}
fullAt(x: number, y: number): boolean {
fullAt(x: number, y: number, rotation: number, debug = false): boolean {
if (y < 0) return false;
if (y >= this.shape.length) return false;
if (y >= this.height(rotation)) return false;
if (x < 0) return false;
if (x >= this.shape[y].length) return false;
// Yes it's ordered y first.
return this.shape[y][x];
if (x >= this.width(rotation)) return false;
// start xy, modifier xy
let [sx, sy, mx, my] = [0, 0, 1, 1];
if (rotation === 1) {
[sx, sy, mx, my] = [this.width(rotation) - 1, 0, -1, 1];
} else if (rotation === 2) {
[sx, sy, mx, my] = [this.width(rotation) - 1, this.height(rotation) - 1, -1, -1];
} else if (rotation === 3) {
[sx, sy, mx, my] = [0, this.height(rotation) - 1, 1, -1];
}
let [qx, qy] = [sx + mx * x, sy + my * y];
if (rotation % 2 === 1) [qx, qy] = [qy, qx];
if (debug) {
console.log("q " + [qx, qy]);
}
return this.shape[qy][qx];
}
width(): number {
// check every line for robustness.
return Math.max(...this.shape.map((line) => line.length));
}
height(): number {
width(rotation: number): number {
if (rotation % 2 === 0) return this.shape[0].length;
return this.shape.length;
}
height(rotation: number): number {
if (rotation % 2 === 0) return this.shape.length;
return this.shape[0].length;
}
// List of direct neighboors of this fragment.
neighboors(): number[][] {
neighboors(rotation: number): number[][] {
const candidates: number[][] = [];
const add = (x: number, y: number): void => {
if (this.fullAt(x, y)) return;
if (this.fullAt(x, y, rotation)) return;
if (candidates.some((coord) => coord[0] === x && coord[1] === y)) return;
candidates.push([x, y]);
};

@ -10,8 +10,8 @@ export interface IStaneksGift {
charge(worldX: number, worldY: number, ram: number): number;
process(p: IPlayer, n: number): void;
effect(fragment: ActiveFragment): number;
canPlace(x: number, y: number, fragment: Fragment): boolean;
place(x: number, y: number, fragment: Fragment): boolean;
canPlace(x: number, y: number, rotation: number, fragment: Fragment): boolean;
place(x: number, y: number, rotation: number, fragment: Fragment): boolean;
fragmentAt(worldX: number, worldY: number): ActiveFragment | null;
deleteAt(worldX: number, worldY: number): boolean;
clear(): void;

@ -70,20 +70,20 @@ export class StaneksGift implements IStaneksGift {
return CalculateEffect(fragment.charge, fragment.fragment().power, boost);
}
canPlace(x: number, y: number, fragment: Fragment): boolean {
if (x + fragment.width() > this.width()) return false;
if (y + fragment.height() > this.height()) return false;
canPlace(x: number, y: number, rotation: number, fragment: Fragment): boolean {
if (x + fragment.width(0) > this.width()) return false;
if (y + fragment.height(0) > this.height()) return false;
if (this.count(fragment) >= fragment.limit) return false;
const newFrag = new ActiveFragment({ x: x, y: y, fragment: fragment });
const newFrag = new ActiveFragment({ x: x, y: y, rotation: rotation, fragment: fragment });
for (const aFrag of this.fragments) {
if (aFrag.collide(newFrag)) return false;
}
return true;
}
place(x: number, y: number, fragment: Fragment): boolean {
if (!this.canPlace(x, y, fragment)) return false;
this.fragments.push(new ActiveFragment({ x: x, y: y, fragment: fragment }));
place(x: number, y: number, rotation: number, fragment: Fragment): boolean {
if (!this.canPlace(x, y, rotation, fragment)) return false;
this.fragments.push(new ActiveFragment({ x: x, y: y, rotation: rotation, fragment: fragment }));
return true;
}

@ -27,10 +27,14 @@ function FragmentOption(props: IOptionProps): React.ReactElement {
<Box display="flex">
<Box sx={{ mx: 2 }}>
<FragmentPreview
width={props.fragment.width()}
height={props.fragment.height()}
width={props.fragment.width(0)}
height={props.fragment.height(0)}
colorAt={(x, y) => {
return !props.fragment.fullAt(x, y) ? "" : props.fragment.type === FragmentType.Booster ? "blue" : "green";
return !props.fragment.fullAt(x, y, 0)
? ""
: props.fragment.type === FragmentType.Booster
? "blue"
: "green";
}}
/>
</Box>

@ -39,11 +39,11 @@ function randomColor(fragment: ActiveFragment): string {
return `rgb(${colors[0] * 256}, ${colors[1] * 256}, ${colors[2] * 256})`;
}
type GridProps = {
interface IProps {
gift: IStaneksGift;
};
}
export function Grid(props: GridProps): React.ReactElement {
export function MainBoard(props: IProps): React.ReactElement {
function calculateGrid(gift: IStaneksGift): any {
const newgrid = zeros([gift.width(), gift.height()]);
for (let i = 0; i < gift.width(); i++) {
@ -60,16 +60,19 @@ export function Grid(props: GridProps): React.ReactElement {
const [grid, setGrid] = React.useState(calculateGrid(props.gift));
const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()]));
const [pos, setPos] = React.useState([0, 0]);
const [rotation, setRotation] = React.useState(0);
const [selectedFragment, setSelectedFragment] = React.useState(NoneFragment);
function moveGhost(worldX: number, worldY: number): void {
if (selectedFragment.type === FragmentType.None || selectedFragment.type === FragmentType.Delete) return;
const newgrid = zeros([props.gift.width(), props.gift.height()]);
for (let i = 0; i < selectedFragment.shape.length; i++) {
for (let j = 0; j < selectedFragment.shape[i].length; j++) {
if (!selectedFragment.shape[i][j]) continue;
if (worldX + j > newgrid.length - 1) continue;
if (worldY + i > newgrid[worldX + j].length - 1) continue;
newgrid[worldX + j][worldY + i] = 1;
for (let y = 0; y < selectedFragment.height(rotation); y++) {
for (let x = 0; x < selectedFragment.width(rotation); x++) {
console.log([x, y]);
if (!selectedFragment.fullAt(x, y, rotation, true)) continue;
if (worldX + x > newgrid.length - 1) continue;
if (worldY + y > newgrid[worldX + x].length - 1) continue;
newgrid[worldX + x][worldY + y] = 1;
}
}
@ -86,8 +89,8 @@ export function Grid(props: GridProps): React.ReactElement {
if (selectedFragment.type == FragmentType.Delete) {
deleteAt(worldX, worldY);
} else {
if (!props.gift.canPlace(worldX, worldY, selectedFragment)) return;
props.gift.place(worldX, worldY, selectedFragment);
if (!props.gift.canPlace(worldX, worldY, rotation, selectedFragment)) return;
props.gift.place(worldX, worldY, rotation, selectedFragment);
}
setGrid(calculateGrid(props.gift));
}
@ -130,6 +133,25 @@ export function Grid(props: GridProps): React.ReactElement {
setGhostGrid(newgrid);
}
React.useEffect(() => {
function doRotate(this: Document, event: KeyboardEvent): void {
if (event.key === "q") {
setRotation((rotation - 1 + 4) % 4);
console.log((rotation - 1 + 4) % 4);
}
if (event.key === "e") {
setRotation((rotation + 1) % 4);
console.log((rotation + 1) % 4);
}
}
document.addEventListener("keydown", doRotate);
return () => document.removeEventListener("keydown", doRotate);
});
// try {
// console.log(selectedFragment);
// } catch (err) {}
return (
<>
<Button onClick={clear}>Clear</Button>

@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { CONSTANTS } from "../../Constants";
import { StaneksGiftEvents } from "../StaneksGiftEvents";
import { Grid } from "./Grid";
import { MainBoard } from "./MainBoard";
import { IStaneksGift } from "../IStaneksGift";
import Typography from "@mui/material/Typography";
@ -30,7 +30,7 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
Bonus time: {convertTimeMsToTimeElapsedString(CONSTANTS._idleSpeed * staneksGift.storedCycles)}
</Typography>
)}
<Grid gift={staneksGift} />
<MainBoard gift={staneksGift} />
</>
);
}

@ -12,8 +12,8 @@ export interface INetscriptStanek {
fragmentDefinitions(): any;
placedFragments(): any;
clear(): void;
canPlace(worldX: number, worldY: number, fragmentId: number): boolean;
place(worldX: number, worldY: number, fragmentId: number): boolean;
canPlace(worldX: number, worldY: number, rotation: number, fragmentId: number): boolean;
place(worldX: number, worldY: number, rotation: number, fragmentId: number): boolean;
fragmentAt(worldX: number, worldY: number): any;
deleteAt(worldX: number, worldY: number): boolean;
}
@ -55,19 +55,19 @@ export function NetscriptStanek(
//checkStanekAPIAccess("clear");
staneksGift.clear();
},
canPlace: function (worldX: any, worldY: any, fragmentId: any): any {
canPlace: function (worldX: any, worldY: any, rotation: any, fragmentId: any): any {
helper.updateDynamicRam("canPlace", getRamCost("stanek", "canPlace"));
//checkStanekAPIAccess("canPlace");
const fragment = FragmentById(fragmentId);
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.canPlace", `Invalid fragment id: ${fragmentId}`);
return staneksGift.canPlace(worldX, worldY, fragment);
return staneksGift.canPlace(worldX, worldY, rotation, fragment);
},
place: function (worldX: any, worldY: any, fragmentId: any): any {
place: function (worldX: any, worldY: any, rotation: any, fragmentId: any): any {
helper.updateDynamicRam("place", getRamCost("stanek", "place"));
//checkStanekAPIAccess("place");
const fragment = FragmentById(fragmentId);
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.place", `Invalid fragment id: ${fragmentId}`);
return staneksGift.place(worldX, worldY, fragment);
return staneksGift.place(worldX, worldY, rotation, fragment);
},
fragmentAt: function (worldX: any, worldY: any): any {
helper.updateDynamicRam("fragmentAt", getRamCost("stanek", "fragmentAt"));