components: multi selecting and winning move recognision!

This commit is contained in:
Artur Tamborski 2020-12-06 23:12:52 +01:00
parent 0c539cc616
commit b1ba2b6209
5 changed files with 80 additions and 80 deletions

View File

@ -16,7 +16,7 @@ export default function App(): JSX.Element {
<Board <Board
numRows={10} numRows={10}
numCols={10} numCols={10}
numLines={5} numLines={2}
/> />
</div> </div>
<div /> <div />

View File

@ -35,12 +35,5 @@
border-radius: 100%; border-radius: 100%;
color: brown; color: brown;
cursor: pointer; cursor: pointer;
&:hover {
border-color: #8b8be5;
outline: none; outline: none;
} }
&:not(hover) {
border-color: #e5e5ea;
}
}

View File

@ -4,15 +4,20 @@ import Line from '../Line/Line';
import './Board.scss'; import './Board.scss';
export type Point = { type Point = {
x: number; x: number;
y: number; y: number;
} }
export type Bar = {
start: Point,
end: Point,
}
type Selection = { type Selection = {
start: Point; bar: Bar,
end: Point; isVisible: boolean;
visible: boolean; isCorrect: boolean;
} }
interface IBoardProps { interface IBoardProps {
@ -24,69 +29,67 @@ interface IBoardProps {
interface IBoardState { interface IBoardState {
cells: string[][]; cells: string[][];
selections: Selection[]; selections: Selection[];
isSelecting: boolean; wasSelecting: boolean;
start: Point; bar: Bar;
end: Point;
} }
const Point0: Point = {x: 0, y: 0}; const SOLUTIONS: Array<Bar> = [
const Selection0: Selection = {start: Point0, end: Point0, visible: false}; {start: {x: 0, y: 0}, end: {x: 5, y: 0}},
{start: {x: 2, y: 2}, end: {x: 4, y: 4}},
]
export default class Board extends React.Component<IBoardProps, IBoardState> { export default class Board extends React.Component<IBoardProps, IBoardState> {
constructor(props: IBoardProps) { constructor(props: IBoardProps) {
super(props); super(props);
const selection: Selection = {
bar: {
start: {x: 0, y: 0},
end: {x: 0, y: 0},
},
isCorrect: false,
isVisible: true,
}
this.state = { this.state = {
cells: Array(props.numCols).fill(Array(props.numRows).fill('A')), cells: Array(props.numCols).fill(Array(props.numRows).fill('A')),
selections: Array(props.numLines).fill(Selection0), selections: [...new Array(props.numLines)].map(() => Object.assign({}, selection)),
isSelecting: false, wasSelecting: false,
start: Point0, bar: {start: {x: 0, y: 0}, end: {x: 0, y: 0}}
end: Point0, }
} }
let s = this.state.selections.slice(); gameOver(): void {
s[0].visible = true; alert("You won!");
this.setState({...this.state})
} }
moveIsValid(pos: Point): boolean { updateSelection(pos: Point, isSelecting: boolean, isMoving: boolean = false): void {
const x = Math.abs(pos.x - this.state.start.x); if (isMoving && !this.state.wasSelecting)
const y = Math.abs(pos.y - this.state.start.y);
const lineIsValid = x === 0 || y === 0 || x === y;
return this.state.isSelecting && lineIsValid;
}
updateSelection(start: Point, end: Point): Array<Selection> {
let selections = this.state.selections.slice();
let s = selections.find(s => s.visible);
if (s !== undefined)
[s.start, s.end, s.visible] = [start, end, true];
return selections;
}
handleMouseDown(start: Point) {
const end = start;
const selections = this.updateSelection(start, start);
this.setState({...this.state, start, end, selections, isSelecting: true});
}
handleMouseUp(end: Point) {
if (!this.moveIsValid(end))
return; return;
const selections = this.updateSelection(this.state.start, end); const selections = this.state.selections.slice();
this.setState({...this.state, end, selections, isSelecting: false}); const s = selections.find(v => !v.isCorrect);
}
handleMouseOver(end: Point) { if (!s)
if (!this.moveIsValid(end)) return this.gameOver();
return;
const selections = this.updateSelection(this.state.start, end); const x = Math.abs(pos.x - this.state.bar.start.x);
this.setState({...this.state, end, selections}); const y = Math.abs(pos.y - this.state.bar.start.y);
const isValidMove = x === 0 || y === 0 || x === y;
const isMouseDown = isSelecting && !this.state.wasSelecting;
const isMouseUp = !isSelecting && this.state.wasSelecting && isValidMove;
const start: Point = isMouseDown ? pos : this.state.bar.start;
const bar: Bar = {start, end: pos};
if (isValidMove)
s.bar = bar;
if (isMouseUp)
s.isCorrect = SOLUTIONS.some(v =>
JSON.stringify(v) === JSON.stringify(bar));
this.setState({...this.state, bar, selections, wasSelecting: isSelecting});
} }
renderCell(s: string, pos: Point): JSX.Element { renderCell(s: string, pos: Point): JSX.Element {
@ -94,9 +97,9 @@ export default class Board extends React.Component<IBoardProps, IBoardState> {
<button <button
className="Cell" className="Cell"
key={`Cell${pos.x}${pos.y}`} key={`Cell${pos.x}${pos.y}`}
onMouseUp={() => this.handleMouseUp(pos)} onMouseUp={() => this.updateSelection(pos, false)}
onMouseDown={() => this.handleMouseDown(pos)} onMouseDown={() => this.updateSelection(pos, true)}
onMouseOver={() => this.handleMouseOver(pos)} onMouseOver={() => this.updateSelection(pos, true, true)}
> >
{s} {s}
</button> </button>
@ -107,9 +110,9 @@ export default class Board extends React.Component<IBoardProps, IBoardState> {
return ( return (
<Line <Line
key={`Line${n}`} key={`Line${n}`}
start={s.start} bar={s.bar}
end={s.end} isVisible={s.isVisible}
visible={s.visible} isCorrect={s.isCorrect}
cellSize={60} cellSize={60}
/> />
); );

View File

@ -3,3 +3,8 @@
stroke-width: 5px; stroke-width: 5px;
fill: none; fill: none;
} }
.CorrectLine {
@extend .Line;
stroke: #60bf4f;
}

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import {Point} from '../Board/Board' import {Bar} from '../Board/Board'
import './Line.scss'; import './Line.scss';
@ -17,10 +17,10 @@ enum Direction {
} }
interface ILineProps { interface ILineProps {
start: Point; bar: Bar;
end: Point;
cellSize: number; cellSize: number;
visible: boolean; isVisible: boolean;
isCorrect: boolean;
} }
export default function Line(props: ILineProps): JSX.Element { export default function Line(props: ILineProps): JSX.Element {
@ -28,6 +28,7 @@ export default function Line(props: ILineProps): JSX.Element {
const margin = props.cellSize / 2; const margin = props.cellSize / 2;
const padding = margin - offset * 2; const padding = margin - offset * 2;
/* pls ignore */
const dir = (v: number) => v < 0 ? 2 : v > 0 ? 1 : 0; const dir = (v: number) => v < 0 ? 2 : v > 0 ? 1 : 0;
const mid = (v: number) => v * props.cellSize + props.cellSize / 2; const mid = (v: number) => v * props.cellSize + props.cellSize / 2;
const pad1 = (v: number) => mid(v) - margin + offset; const pad1 = (v: number) => mid(v) - margin + offset;
@ -35,17 +36,15 @@ export default function Line(props: ILineProps): JSX.Element {
const pad3 = (v: number) => mid(v) + padding + v * padding; const pad3 = (v: number) => mid(v) + padding + v * padding;
const pad4 = (v: number) => mid(v - 1) + margin; const pad4 = (v: number) => mid(v - 1) + margin;
const ox = dir(props.start.x - props.end.x); const [startX, startY] = [props.bar.start.x, props.bar.start.y];
const oy = dir(props.start.y - props.end.y); const [endX, endY] = [props.bar.end.x, props.bar.end.y];
const [ox, oy] = [dir(startX - endX), dir(startY - endY)];
const d = Number(`${ox}${oy}`) as Direction; const d = Number(`${ox}${oy}`) as Direction;
const minX = Math.min(props.start.x, props.end.x); const maxY = Math.max(startY, endY);
const minY = Math.min(props.start.y, props.end.y); const [minX, minY] = [Math.min(startX, endX), Math.min(startY, endY)];
const maxY = Math.max(props.start.y, props.end.y); const [absX, absY] = [Math.abs(startX - endX), Math.abs(startY - endY)];
const absX = Math.abs(props.start.x - props.end.x); const [signX, signY] = [Math.sign(startX - endX), Math.sign(startY - endY)];
const absY = Math.abs(props.start.y - props.end.y);
const signX = Math.sign(props.start.x - props.end.x);
const signY = Math.sign(props.start.y - props.end.y);
/* uhh, edge cases for two directions, idk how to fix it */ /* uhh, edge cases for two directions, idk how to fix it */
const x = d === Direction.LeftDown ? pad4(minX) : pad1(minX); const x = d === Direction.LeftDown ? pad4(minX) : pad1(minX);
@ -78,12 +77,12 @@ export default function Line(props: ILineProps): JSX.Element {
return ( return (
<rect <rect
className="Line" className={props.isCorrect ? 'CorrectLine' : 'Line'}
rx={margin - offset} rx={margin - offset}
ry={margin - offset} ry={margin - offset}
{...{x, y, width, height}} {...{x, y, width, height}}
transform={`translate(${tx}, ${ty}), rotate(${r}, ${rx}, ${ry})`} transform={`translate(${tx}, ${ty}), rotate(${r}, ${rx}, ${ry})`}
visibility={props.visible ? 'visible' : 'hidden'} visibility={props.isVisible ? 'visible' : 'hidden'}
/> />
); );
} }