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();
function App() {
const [rows, setRows] = 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 [flags, setFlags] = useState(5);
const [mines, setMines] = useState(5);
const [rows, setRows] = useState(10);
const [updateDummyBoard, setUpdateDummyBoard] = useState(true);
const [activeGame, setActiveGame] = useState(null);
const handleSetRows = value => {
// TODO can't be less than 3
if (value < 3 || value > 99) {
return;
}
setRows(value);
setUpdateDummyBoard(true);
};
const handleSetCols = value => {
// TODO can't be less than 3
if (value < 3 || value > 99) {
return;
}
setCols(value);
setUpdateDummyBoard(true);
};
const generateDummyBoard = () => {
setDummyBoard([]);
const board = [];
for (let row = 0; row < rows; row++) {
@@ -41,41 +44,67 @@ function App() {
};
const handleSetMines = event => {
// TODO can't be less than 1
setMines(parseInt(event.target.value));
setFlags(parseInt(event.target.value));
generateDummyBoard();
const value = parseInt(event.target.value);
if (value < 1 || value > 99) {
return;
}
setMines(value);
setFlags(value);
};
useEffect(() => {
function bootstrap() {
setMounted(true);
generateDummyBoard();
}
if (!mounted) {
bootstrap();
setUpdateDummyBoard(false);
handleCreateNewGame();
}
if (updateDummyBoard) {
setUpdateDummyBoard(false);
generateDummyBoard();
bootstrap();
}
if (activeGame && activeGame.win === true) {
document.getElementsByClassName('reset')[0].innerHTML = '😎';
}
});
const handleCellClick = (row, col) => {
if (activeGame.status === 3 || activeGame.win === true) {
return;
}
const cell = document.getElementById(`cell_${row}_${col}`);
if (cell.classList.contains('flagged')) {
return;
}
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) => {
event.preventDefault();
if (activeGame.win === true) {
return;
}
const cell = document.getElementById(`cell_${row}_${col}`);
if (cell.classList.contains('revealed')) {
return;
@@ -83,18 +112,22 @@ function App() {
if (cell.classList.contains('flagged')) {
cell.classList.remove('flagged');
} else {
setFlags(flags + 1);
} else if (flags > 0) {
cell.classList.add('flagged');
setFlags(flags - 1);
}
};
const handleCreateNewGame = event => {
gameClient
.createNewGame(rows, cols, mines)
.then(response => console.log(response));
const handleCreateNewGame = () => {
gameClient.createNewGame(rows, cols, mines).then(response => {
setActiveGame(response);
setFlags(mines);
generateDummyBoard();
});
};
if (!mounted) {
if (!activeGame) {
return null;
}
@@ -111,6 +144,7 @@ function App() {
setCols={handleSetCols}
setMines={setMines}
setRows={handleSetRows}
setUpdateDummyBoard={setUpdateDummyBoard}
/>
<Board
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-mid: #e3e3e3;
$gray-dark: #333;
$gray-border: #808080;
$gray: #d1d1d1;
$red: #ec433c;

View File

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

View File

@@ -2,7 +2,20 @@
.board {
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 {
display: flex;
justify-content: center;
@@ -14,8 +27,52 @@
}
&-cell.revealed {
margin: 1px;
border: 2px solid $gray-dark;
padding: 2px;
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 {

View File

@@ -11,45 +11,51 @@ const Header = ({
mines,
rows,
setCols,
setRows
setRows,
setUpdateDummyBoard
}) => {
return (
<div className="header">
<div className="flags led-panel box">{flags}</div>
<div className="settings">
<div className="custom">
<input
className="box"
onChange={event => setRows(parseInt(event.target.value))}
type="number"
value={rows}
/>
<input
className="box"
onChange={event => setCols(parseInt(event.target.value))}
type="number"
value={cols}
/>
<input
className="box"
onChange={event => handleSetMines(event)}
type="number"
value={mines}
/>
<input
className="box"
onChange={event => setRows(parseInt(event.target.value))}
type="number"
value={rows}
/>
<input
className="box"
onChange={event => setCols(parseInt(event.target.value))}
type="number"
value={cols}
/>
<input
className="box"
onChange={event => handleSetMines(event)}
type="number"
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 className="timer led-panel box">000</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>
);
};
@@ -63,7 +69,8 @@ Header.propTypes = {
rows: PropTypes.number,
setCols: PropTypes.func.isRequired,
setMines: PropTypes.func.isRequired,
setRows: PropTypes.func.isRequired
setRows: PropTypes.func.isRequired,
setUpdateDummyBoard: PropTypes.func.isRequired
};
export default Header;

View File

@@ -3,42 +3,54 @@
.header {
max-width: 500px;
width: 100%;
display: flex;
justify-content: space-between;
padding: 5px;
margin: 0 auto;
.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;
}
display: flex;
justify-content: space-between;
flex-direction: column;
.settings {
max-width: 221px;
display: flex;
justify-content: space-between;
justify-content: center;
margin: 10px auto;
.custom {
display: flex;
justify-content: space-between;
input {
width: 40px;
height: 30px;
line-height: 30px;
text-align: center;
background: $gray-light;
margin: 0 2px;
}
input {
width: 30px;
text-align: center;
}
.button {
cursor: pointer;
margin: 0 2px;
}
}
.reset {
width: 30px;
line-height: 30px;
text-align: center;
cursor: pointer;
.canvas {
display: flex;
justify-content: space-between;
flex-direction: row;
.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 {
base = '/';
CLICK_NAIVE = 8;
createNewGame(rows, cols, mines) {
return this.client
.post(`${this.base}games`, { rows, cols, mines })
.then(getResponseData);
}
cellClick(row, col, gameId) {
return this.client
.post(`${this.base}games/${gameId}/events`, { row, col })
.then(getResponseData);
}
}