components: multi selecting and winning move recognision!
This commit is contained in:
parent
0c539cc616
commit
b1ba2b6209
@ -16,7 +16,7 @@ export default function App(): JSX.Element {
|
||||
<Board
|
||||
numRows={10}
|
||||
numCols={10}
|
||||
numLines={5}
|
||||
numLines={2}
|
||||
/>
|
||||
</div>
|
||||
<div />
|
||||
|
@ -35,12 +35,5 @@
|
||||
border-radius: 100%;
|
||||
color: brown;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: #8b8be5;
|
||||
outline: none;
|
||||
}
|
||||
&:not(hover) {
|
||||
border-color: #e5e5ea;
|
||||
}
|
||||
outline: none;
|
||||
}
|
||||
|
@ -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}
|
||||
/>
|
||||
);
|
||||
|
@ -3,3 +3,8 @@
|
||||
stroke-width: 5px;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.CorrectLine {
|
||||
@extend .Line;
|
||||
stroke: #60bf4f;
|
||||
}
|
||||
|
@ -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'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user