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
numRows={10}
numCols={10}
numLines={5}
numLines={2}
/>
</div>
<div />

View File

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

View File

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

View File

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

View File

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