Almost done

This commit is contained in:
2020-11-07 19:21:08 -03:00
parent cd73c39b23
commit 0e6476cedb
8 changed files with 229 additions and 106 deletions

View File

@@ -6,27 +6,30 @@ import { GameService } from 'services';
const gameClient = new GameService(); const gameClient = new GameService();
function App() { function App() {
const [rows, setRows] = useState(10);
const [cols, setCols] = useState(10); const [cols, setCols] = useState(10);
const [mines, setMines] = useState(5);
const [flags, setFlags] = useState(5);
const [mounted, setMounted] = useState(false);
const [dummyBoard, setDummyBoard] = useState([]); const [dummyBoard, setDummyBoard] = useState([]);
const [flags, setFlags] = useState(5);
const [mines, setMines] = useState(5);
const [rows, setRows] = useState(10);
const [updateDummyBoard, setUpdateDummyBoard] = useState(true); const [updateDummyBoard, setUpdateDummyBoard] = useState(true);
const [activeGame, setActiveGame] = useState(null);
const handleSetRows = value => { const handleSetRows = value => {
// TODO can't be less than 3 if (value < 3 || value > 99) {
return;
}
setRows(value); setRows(value);
setUpdateDummyBoard(true);
}; };
const handleSetCols = value => { const handleSetCols = value => {
// TODO can't be less than 3 if (value < 3 || value > 99) {
return;
}
setCols(value); setCols(value);
setUpdateDummyBoard(true);
}; };
const generateDummyBoard = () => { const generateDummyBoard = () => {
setDummyBoard([]);
const board = []; const board = [];
for (let row = 0; row < rows; row++) { for (let row = 0; row < rows; row++) {
@@ -41,41 +44,67 @@ function App() {
}; };
const handleSetMines = event => { const handleSetMines = event => {
// TODO can't be less than 1 const value = parseInt(event.target.value);
setMines(parseInt(event.target.value)); if (value < 1 || value > 99) {
setFlags(parseInt(event.target.value)); return;
generateDummyBoard(); }
setMines(value);
setFlags(value);
}; };
useEffect(() => { useEffect(() => {
function bootstrap() { function bootstrap() {
setMounted(true); setUpdateDummyBoard(false);
generateDummyBoard(); handleCreateNewGame();
}
if (!mounted) {
bootstrap();
} }
if (updateDummyBoard) { if (updateDummyBoard) {
setUpdateDummyBoard(false); bootstrap();
generateDummyBoard(); }
if (activeGame && activeGame.win === true) {
document.getElementsByClassName('reset')[0].innerHTML = '😎';
} }
}); });
const handleCellClick = (row, col) => { const handleCellClick = (row, col) => {
if (activeGame.status === 3 || activeGame.win === true) {
return;
}
const cell = document.getElementById(`cell_${row}_${col}`); const cell = document.getElementById(`cell_${row}_${col}`);
if (cell.classList.contains('flagged')) { if (cell.classList.contains('flagged')) {
return; return;
} }
if (!cell.classList.contains('revealed')) { if (!cell.classList.contains('revealed')) {
cell.classList.add('revealed'); cell.classList.add('busy');
gameClient.cellClick(row, col, activeGame.id).then(response => {
cell.classList.add('revealed');
cell.classList.remove('busy');
if (response.active_game.board_progress[row][col] === 0) {
cell.classList.add('empty');
} else if (response.active_game.board_progress[row][col] > 0) {
const points = `${response.active_game.board_progress[row][col]}`;
cell.classList.add('point');
cell.classList.add(`point-${points}`);
cell.innerHTML = points;
} else {
cell.classList.add('mined');
document.getElementsByClassName('reset')[0].innerHTML = '👻';
}
setActiveGame(response.active_game);
});
} }
}; };
const handleCellContextMenu = (event, row, col) => { const handleCellContextMenu = (event, row, col) => {
event.preventDefault(); event.preventDefault();
if (activeGame.win === true) {
return;
}
const cell = document.getElementById(`cell_${row}_${col}`); const cell = document.getElementById(`cell_${row}_${col}`);
if (cell.classList.contains('revealed')) { if (cell.classList.contains('revealed')) {
return; return;
@@ -83,18 +112,22 @@ function App() {
if (cell.classList.contains('flagged')) { if (cell.classList.contains('flagged')) {
cell.classList.remove('flagged'); cell.classList.remove('flagged');
} else { setFlags(flags + 1);
} else if (flags > 0) {
cell.classList.add('flagged'); cell.classList.add('flagged');
setFlags(flags - 1);
} }
}; };
const handleCreateNewGame = event => { const handleCreateNewGame = () => {
gameClient gameClient.createNewGame(rows, cols, mines).then(response => {
.createNewGame(rows, cols, mines) setActiveGame(response);
.then(response => console.log(response)); setFlags(mines);
generateDummyBoard();
});
}; };
if (!mounted) { if (!activeGame) {
return null; return null;
} }
@@ -111,6 +144,7 @@ function App() {
setCols={handleSetCols} setCols={handleSetCols}
setMines={setMines} setMines={setMines}
setRows={handleSetRows} setRows={handleSetRows}
setUpdateDummyBoard={setUpdateDummyBoard}
/> />
<Board <Board
cols={cols} cols={cols}

BIN
src/assets/images/mine.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,6 +1,7 @@
$gray-light: #f6f6f6; $gray-light: #f6f6f6;
$gray-mid: #e3e3e3; $gray-mid: #e3e3e3;
$gray-dark: #333; $gray-dark: #333;
$gray-border: #808080;
$gray: #d1d1d1; $gray: #d1d1d1;
$red: #ec433c; $red: #ec433c;

View File

@@ -6,22 +6,26 @@ import './styles.scss';
const Board = ({ dummyBoard, handleCellClick, handleCellContextMenu }) => { const Board = ({ dummyBoard, handleCellClick, handleCellContextMenu }) => {
return ( return (
<div className="board"> <div className="board">
{dummyBoard.map((cols, rowId) => ( <div className="board-col-left"> </div>
<div className="board-row" key={`row_${rowId}`}> <div className="board-col-center">
{cols.map((col, colId) => ( {dummyBoard.map((cols, rowId) => (
<div <div className="board-row" key={`row_${rowId}`}>
className="board-row-cell box" {cols.map((col, colId) => (
id={`cell_${rowId}_${colId}`} <div
key={`cell_${rowId}_${colId}`} className="board-row-cell box"
onClick={() => handleCellClick(rowId, colId)} id={`cell_${rowId}_${colId}`}
onContextMenu={event => key={`cell_${rowId}_${colId}`}
handleCellContextMenu(event, rowId, colId) onClick={() => handleCellClick(rowId, colId)}
}> onContextMenu={event =>
{' '} handleCellContextMenu(event, rowId, colId)
</div> }>
))} {' '}
</div> </div>
))} ))}
</div>
))}
</div>
<div className="board-col-right"> </div>
</div> </div>
); );
}; };

View File

@@ -2,7 +2,20 @@
.board { .board {
display: flex; display: flex;
flex-direction: column;
&-col-left,
&-col-right {
flex: 1 1 auto;
}
&-col-center {
flex: 0 0 auto;
border-width: 3px;
border-style: solid;
border-color: darken($gray, 20%) lighten($gray, 20%) lighten($gray, 20%)
darken($gray, 20%);
}
&-row { &-row {
display: flex; display: flex;
justify-content: center; justify-content: center;
@@ -14,8 +27,52 @@
} }
&-cell.revealed { &-cell.revealed {
margin: 1px; padding: 2px;
border: 2px solid $gray-dark; border: 1px solid $gray-border;
}
&-cell.busy {
background-color: $gray-mid;
margin: 3px;
border: none;
}
&-cell.point {
text-align: center;
font-size: 90%;
font-weight: bold;
}
&-cell.point-1 {
color: #0000ff;
}
&-cell.point-2 {
color: #037d02;
}
&-cell.point-3 {
color: #fd0000;
}
&-cell.point-4 {
color: #010180;
}
&-cell.point-5 {
color: #830101;
}
&-cell.point-6 {
color: #038081;
}
&-cell.point-7 {
color: #000000;
}
&-cell.point-8 {
color: #808080;
}
&-cell.mined {
background-image: url(../../assets/images/mine.png);
background-repeat: no-repeat;
background-position: center center;
background-size: 90%;
} }
&-cell { &-cell {

View File

@@ -11,45 +11,51 @@ const Header = ({
mines, mines,
rows, rows,
setCols, setCols,
setRows setRows,
setUpdateDummyBoard
}) => { }) => {
return ( return (
<div className="header"> <div className="header">
<div className="flags led-panel box">{flags}</div>
<div className="settings"> <div className="settings">
<div className="custom"> <input
<input className="box"
className="box" onChange={event => setRows(parseInt(event.target.value))}
onChange={event => setRows(parseInt(event.target.value))} type="number"
type="number" value={rows}
value={rows} />
/> <input
<input className="box"
className="box" onChange={event => setCols(parseInt(event.target.value))}
onChange={event => setCols(parseInt(event.target.value))} type="number"
type="number" value={cols}
value={cols} />
/> <input
<input className="box"
className="box" onChange={event => handleSetMines(event)}
onChange={event => handleSetMines(event)} type="number"
type="number" value={mines}
value={mines} />
/> <button
className="button box"
onClick={() => setUpdateDummyBoard(true)}>
Apply
</button>
</div>
<div className="canvas">
<div className="flags led-panel box">{flags}</div>
<div
className="reset box"
onMouseDown={event => {
event.target.innerHTML = '😮';
}}
onMouseUp={event => {
event.target.innerHTML = '🙂';
handleCreateNewGame(event);
}}>
🙂
</div> </div>
<div className="timer led-panel box">000</div>
</div> </div>
<div
className="reset box"
onMouseDown={event => {
event.target.innerHTML = '😮';
}}
onMouseUp={event => {
event.target.innerHTML = '🙂';
handleCreateNewGame(event);
}}>
🙂
</div>
<div className="timer led-panel box">000</div>
</div> </div>
); );
}; };
@@ -63,7 +69,8 @@ Header.propTypes = {
rows: PropTypes.number, rows: PropTypes.number,
setCols: PropTypes.func.isRequired, setCols: PropTypes.func.isRequired,
setMines: PropTypes.func.isRequired, setMines: PropTypes.func.isRequired,
setRows: PropTypes.func.isRequired setRows: PropTypes.func.isRequired,
setUpdateDummyBoard: PropTypes.func.isRequired
}; };
export default Header; export default Header;

View File

@@ -3,42 +3,54 @@
.header { .header {
max-width: 500px; max-width: 500px;
width: 100%; width: 100%;
display: flex;
justify-content: space-between;
padding: 5px; padding: 5px;
margin: 0 auto; margin: 0 auto;
display: flex;
.led-panel { justify-content: space-between;
font-family: 'Space Mono', monospace; flex-direction: column;
background: $gray-dark;
color: $red;
text-shadow: 0 0 2px rgba($red, 1);
line-height: 30px;
letter-spacing: 0.05em;
text-align: center;
width: 30px;
}
.settings { .settings {
max-width: 221px; max-width: 221px;
display: flex; display: flex;
justify-content: space-between; justify-content: center;
margin: 10px auto;
.custom { input {
display: flex; width: 40px;
justify-content: space-between; height: 30px;
line-height: 30px;
text-align: center;
background: $gray-light;
margin: 0 2px;
}
input { .button {
width: 30px; cursor: pointer;
text-align: center; margin: 0 2px;
}
} }
} }
.reset { .canvas {
width: 30px; display: flex;
line-height: 30px; justify-content: space-between;
text-align: center; flex-direction: row;
cursor: pointer;
.led-panel {
font-family: 'Space Mono', monospace;
background: $gray-dark;
color: $red;
text-shadow: 0 0 2px rgba($red, 1);
line-height: 30px;
letter-spacing: 0.05em;
text-align: center;
width: 30px;
}
.reset {
width: 30px;
line-height: 30px;
text-align: center;
cursor: pointer;
}
} }
} }

View File

@@ -3,9 +3,17 @@ import { RestClient, getResponseData } from './base';
export class GameService extends RestClient { export class GameService extends RestClient {
base = '/'; base = '/';
CLICK_NAIVE = 8;
createNewGame(rows, cols, mines) { createNewGame(rows, cols, mines) {
return this.client return this.client
.post(`${this.base}games`, { rows, cols, mines }) .post(`${this.base}games`, { rows, cols, mines })
.then(getResponseData); .then(getResponseData);
} }
cellClick(row, col, gameId) {
return this.client
.post(`${this.base}games/${gameId}/events`, { row, col })
.then(getResponseData);
}
} }