Skip to content

Commit

Permalink
Bonus to copy existing tile from board (fix #65)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcreedcmu committed Nov 23, 2023
1 parent 3cb8b91 commit da0b307
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 29 deletions.
Binary file added public/assets/copy-cursor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/assets/toolbar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ export function Game(props: GameProps): JSX.Element {
if (tool == 'bomb') {
return 'url(assets/bomb-cursor.png) 16 16, pointer';
}
if (tool == 'copy') {
return 'url(assets/copy-cursor.png) 16 16, pointer';
}
if (tool == 'hand') {
return 'grab';
}
Expand Down
42 changes: 25 additions & 17 deletions src/core/bonus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ import { Layer, mkLayer } from './layer';
import { CoreState, Tile, TileEntity } from './state';
import { MoveTile } from './state-helpers';

export type Bonus =
export type ScoringBonus =
| { t: 'bonus' }
| { t: 'bomb' }
| { t: 'empty' }
| { t: 'block' }
| { t: 'required', letter: string }
| { t: 'consonant' }
| { t: 'vowel' }
| { t: 'copy' }
;

export type Bonus =
| ScoringBonus
| { t: 'empty' }
| { t: 'block' }
;

export function bonusGenerator(p: Point, seed: number): Bonus {
Expand Down Expand Up @@ -44,6 +49,9 @@ export function bonusGenerator(p: Point, seed: number): Bonus {
if (ph < 0.5) {
return { t: 'required', letter: deterministicLetterSample(ph * 1e9) };
}
else if (ph < 0.53) {
return { t: 'copy' };
}
else {
return { t: 'block' };
}
Expand All @@ -64,40 +72,40 @@ export function isBlocking(tile: MoveTile, bonus: Bonus): boolean {
return true;
}

type Scoring =
| { t: 'bonus', p: Point }
| { t: 'bomb', p: Point }
| { t: 'required', p: Point }
| { t: 'vowel', p: Point }
| { t: 'consonant', p: Point }
;
type Scoring = {
bonus: ScoringBonus,
p: Point
};

export function adjacentScoringOfBonus(bonus: Bonus, p: Point): Scoring[] {
switch (bonus.t) {
case 'bonus': return [{ t: 'bonus', p }];
case 'bomb': return [{ t: 'bomb', p }];
case 'vowel': return [{ t: 'vowel', p }];
case 'consonant': return [{ t: 'consonant', p }];
case 'bonus': return [{ bonus, p }];
case 'bomb': return [{ bonus, p }];
case 'vowel': return [{ bonus, p }];
case 'consonant': return [{ bonus, p }];
case 'copy': return [{ bonus, p }];
default: return [];
}
}

export function overlapScoringOfBonus(bonus: Bonus, p: Point): Scoring[] {
switch (bonus.t) {
case 'required': return [{ t: 'required', p }];
case 'required': return [{ bonus, p }];
default: return [];
}
}

export function resolveScoring(state: Draft<CoreState>, scoring: Scoring): void {
switch (scoring.t) {
const bonus = scoring.bonus;
switch (bonus.t) {
case 'bonus': state.score++; return;
case 'bomb': state.inventory.bombs++; return;
case 'required': state.score += 10; return;
case 'vowel': state.inventory.vowels += 5; return
case 'consonant': state.inventory.consonants += 5; return
case 'copy': state.inventory.copies += 3; return
}
unreachable(scoring);
unreachable(bonus);
}

// Bonus Layer Generation
Expand Down
23 changes: 21 additions & 2 deletions src/core/intent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { produce } from '../util/produce';
import { vm } from '../util/vutil';
import { GameState, TileEntity } from './state';
import { tryKillTileOfState } from './kill-helpers';
import { Tool, bombIntent, dynamiteIntent } from './tools';
import { Tool, bombIntent, copyIntent, dynamiteIntent } from './tools';
import { SelectionOperation, selectionOperationOfMods } from './selection';
import { vacuous_down, deselect } from './reduce';
import { withCoreState } from './state-helpers';
import { drawSpecificOfState, withCoreState } from './state-helpers';
import { tileAtPoint } from './tile-helpers';

export type KillIntent =
| { t: 'kill', radius: number, cost: number }
Expand All @@ -18,6 +19,7 @@ export type Intent =
| { t: 'panWorld' }
| { t: 'exchangeTiles', id: string }
| { t: 'startSelection', opn: SelectionOperation }
| { t: 'copy' }
| KillIntent
;

Expand Down Expand Up @@ -50,6 +52,7 @@ export function getIntentOfMouseDown(tool: Tool, wp: WidgetPoint, button: number
return bombIntent;
case 'vowel': throw new Error(`shoudn't be able have vowel tool active`);
case 'consonant': throw new Error(`shoudn't be able have consonant tool active`);
case 'copy': return copyIntent;
}
}

Expand Down Expand Up @@ -110,5 +113,21 @@ export function reduceIntent(state: GameState, intent: Intent, wp: WidgetPoint):
opn: intent.opn,
};
});
case 'copy': {
if (wp.t == 'world') {
const hoverTile = tileAtPoint(state.coreState, wp.p_in_local);
if (hoverTile == undefined)
return state;
const newCs = drawSpecificOfState(state.coreState, hoverTile.letter);
if (newCs == state.coreState) return state;
return withCoreState(state, cs => produce(newCs, s => {
s.inventory.copies--;
}));
}
else {
return state;
}

}
}
}
1 change: 1 addition & 0 deletions src/core/reduce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ function reduceGameAction(state: GameState, action: GameAction): effectful.Resul
s.inventory.bombs = 15;
s.inventory.vowels = 15;
s.inventory.consonants = 15;
s.inventory.copies = 15;
}))));
}
return gs(state);
Expand Down
5 changes: 5 additions & 0 deletions src/core/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,10 @@ export function tryReduceShortcut(state: GameState, code: string): GameState | u
return withCoreState(state, cs => reduceToolSelect(cs, 'consonant'));
}
}
if (code == 'x') {
if (state.coreState.inventory.copies >= 1) {
return withCoreState(state, cs => reduceToolSelect(cs, 'copy'));
}
}
return undefined;
}
9 changes: 9 additions & 0 deletions src/core/state-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ export function drawOfState(state: CoreState, drawForce?: DrawForce): CoreState
}));
}

export function drawSpecificOfState(state: CoreState, letter: string): CoreState {
const handLength = get_hand_tiles(state).length;
if (handLength >= HAND_TILE_LIMIT)
return state;
return checkValid(produce(state, s => {
addHandTile(s, ensureTileId({ letter, p_in_world_int: { x: 0, y: handLength } }));
}));
}

const directions: Point[] = [[1, 0], [-1, 0], [0, 1], [0, -1]].map(([x, y]) => ({ x, y }));

export function resolveValid(state: CoreState): CoreState {
Expand Down
2 changes: 2 additions & 0 deletions src/core/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export type CoreState = {
bombs: number,
vowels: number,
consonants: number,
copies: number,
}
bonusLayerName: string,
};
Expand Down Expand Up @@ -139,6 +140,7 @@ export function mkGameState(seed?: number): GameState {
bombs: 0,
vowels: 0,
consonants: 0,
copies: 0,
},
bonusLayerName: 'game',
},
Expand Down
9 changes: 7 additions & 2 deletions src/core/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const tools = [
'bomb',
'vowel',
'consonant',
'copy',
] as const;

export type Tool = (typeof tools)[number];
Expand All @@ -35,6 +36,7 @@ export function getCurrentTool(state: CoreState): Tool {
export const dynamiteIntent: Intent & { t: 'kill' } = { t: 'kill', radius: 0, cost: 1 };
export const BOMB_RADIUS = 2;
export const bombIntent: Intent & { t: 'bomb' } = { t: 'bomb' };
export const copyIntent: Intent & { t: 'copy' } = { t: 'copy' };

export function getCurrentTools(state: CoreState): Tool[] {
if (state.lost) {
Expand All @@ -53,6 +55,9 @@ export function getCurrentTools(state: CoreState): Tool[] {
if (state.inventory.consonants > 0) {
tools.push('consonant');
}
if (state.inventory.copies > 0) {
tools.push('copy');
}
return tools;
}

Expand All @@ -66,14 +71,14 @@ export function reduceToolSelect(state: CoreState, tool: Tool): CoreState {
switch (tool) {
case 'consonant': {
const newState = drawOfState(state, 'consonant');
if (newState == state) return newState;
if (newState == state) return state;
return produce(newState, s => {
s.inventory.consonants--;
});
}
case 'vowel': {
const newState = drawOfState(state, 'vowel');
if (newState == state) return newState;
if (newState == state) return state;
return produce(newState, s => {
s.inventory.vowels--;
});
Expand Down
5 changes: 4 additions & 1 deletion src/ui/drawBonus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ export function drawBonus(d: CanvasRenderingContext2D, bonus: Bonus, pan_canvas_
drawImage(d, toolbarImg, rectOfTool('vowel'), rect_in_canvas);
return;
}

case 'copy': {
drawImage(d, toolbarImg, rectOfTool('copy'), rect_in_canvas);
return;
}
}
unreachable(bonus);
}
1 change: 1 addition & 0 deletions src/ui/instructions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ function exampleState(): GameState {
bombs: 3,
vowels: 0,
consonants: 0,
copies: 0,
},
},
mouseState: {
Expand Down
19 changes: 12 additions & 7 deletions src/ui/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ function drawToolbar(d: CanvasRenderingContext2D, state: CoreState): void {
else if (tool == 'consonant') {
drawToolbarCount(d, rect_in_canvas, state.inventory.consonants);
}
else if (tool == 'copy') {
drawToolbarCount(d, rect_in_canvas, state.inventory.copies);
}

// indicate current tool
if (tool == currentTool) {
Expand Down Expand Up @@ -193,13 +196,15 @@ export function rawPaint(ci: CanvasInfo, state: GameState) {
}

function drawPauseButton() {
d.textAlign = 'center';
d.textBaseline = 'middle';
if (!cs.lost) {
fillText(d, '⏸', midpointOfRect(pause_button_bds_in_canvas), 'black', '48px sans-serif');
}
else {
fillText(d, '⟳', midpointOfRect(pause_button_bds_in_canvas), 'black', '48px sans-serif');
if (cs.panic) {
d.textAlign = 'center';
d.textBaseline = 'middle';
if (!cs.lost) {
fillText(d, '⏸', midpointOfRect(pause_button_bds_in_canvas), 'black', '48px sans-serif');
}
else {
fillText(d, '⟳', midpointOfRect(pause_button_bds_in_canvas), 'black', '48px sans-serif');
}
}
}

Expand Down

0 comments on commit da0b307

Please sign in to comment.