From 23547f42374b30488ecdfd49fb064a08f93ec4ed Mon Sep 17 00:00:00 2001 From: Lang <7system7@gmail.com> Date: Thu, 9 Apr 2026 15:08:00 +0200 Subject: [PATCH] chg: usr: created the first working solution since 7 yrs #4 --- assets/js/mine-seeker/app.js | 844 +++++++++++---------- assets/js/mine-seeker/grid/grid-control.js | 792 +++++++++---------- assets/js/mine-seeker/grid/grid-field.js | 283 +++---- assets/js/mine-seeker/grid/grid.js | 176 ++--- assets/js/mine-seeker/user/user-control.js | 136 ++-- assets/js/mine-seeker/user/user.js | 154 ++-- 6 files changed, 1206 insertions(+), 1179 deletions(-) diff --git a/assets/js/mine-seeker/app.js b/assets/js/mine-seeker/app.js index ba85a36..9d22647 100644 --- a/assets/js/mine-seeker/app.js +++ b/assets/js/mine-seeker/app.js @@ -3,454 +3,464 @@ import Grid from './grid/grid'; import GridControl from './grid/grid-control'; class MineSeeker extends React.Component { - constructor(props) { - super(props); + constructor(props) { + super(props); - let gameAssoc = props.gameId !== '' ? props.gameId : this.makeGameAssoc(50); - let channel = "mineseeker/channel/" + gameAssoc; + let gameAssoc = '' !== props.gameId ? props.gameId : this.makeGameAssoc(50); + let channel = 'mineseeker/channel/' + gameAssoc; - this.state = { - env: props.env, - ssl: props.ssl, - gameInherited: props.gameId !== '', - gameAssoc: gameAssoc, - channel: channel, - session: null, - createGrid: false, - stepCache: [], - connectionLost: false, - end: false - } + this.state = { + env: props.env, + ssl: props.ssl, + gameInherited: '' !== props.gameId, + gameAssoc: gameAssoc, + channel: channel, + session: null, + createGrid: false, + stepCache: [], + connectionLost: false, + end: false, + }; + } + + makeGameAssoc(len) { + let text = ''; + let possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < len; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); } + return text; + } - makeGameAssoc(len) { - let text = ""; - let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (let i = 0; i < len; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; - } + currectGridSize() { + let $field = $('#mine-wrapper .grid'); + $field.height($field.width()); - currectGridSize() { - let $field = $('#mine-wrapper .grid'); - $field.height($field.width()); + $field = $('#mine-wrapper .grid .field-wrapper'); + $field.height($field.width()); - $field = $('#mine-wrapper .grid .field-wrapper'); - $field.height($field.width()); + $('#mine-wrapper .grid .field-wrapper .field') + .height($field.width()) + .css('line-height', ($field.width() - 2) + 'px'); + } - $('#mine-wrapper .grid .field-wrapper .field') - .height($field.width()) - .css('line-height', ($field.width() - 2) + 'px'); - } + /** + * STEP + * + * @param coords + * @returns {{red: *, blue: *}} + */ + makePointsCalcAndStep(coords) { + let users = this.refs.gridControl.refs.userControl, + activePlayer = users.state.activePlayer ? 'blue' : 'red', + inactivePlayer = users.state.activePlayer ? 'red' : 'blue', + redPoints = 'red' === activePlayer + ? users.refs[activePlayer].state.mines + : users.refs[inactivePlayer].state.mines, + bluePoints = 'blue' === activePlayer + ? users.refs[activePlayer].state.mines + : users.refs[inactivePlayer].state.mines; - /** - * STEP - * - * @param coords - * @returns {{red: *, blue: *}} - */ - makePointsCalcAndStep(coords) { - let users = this.refs.gridControl.refs.userControl, - activePlayer = users.state.activePlayer ? 'blue' : 'red', - inactivePlayer = users.state.activePlayer ? 'red' : 'blue', - redPoints = activePlayer === 'red' - ? users.refs[activePlayer].state.mines - : users.refs[inactivePlayer].state.mines, - bluePoints = activePlayer === 'blue' - ? users.refs[activePlayer].state.mines - : users.refs[inactivePlayer].state.mines; + this.refs.gridControl.stepEvent(coords); - this.refs.gridControl.stepEvent(coords); + let mineCache = this.refs.gridControl.state.foundUserMineCache; + redPoints += 'red' === activePlayer ? mineCache : 0; + bluePoints += 'blue' === activePlayer ? mineCache : 0; - let mineCache = this.refs.gridControl.state.foundUserMineCache; - redPoints += activePlayer === 'red' ? mineCache : 0; - bluePoints += activePlayer === 'blue' ? mineCache : 0; + return { red: redPoints, blue: bluePoints }; + } - return {red: redPoints, blue: bluePoints}; - } + /** + * START + * + * @param payload + */ + makeGameStart(payload) { + /** every time the blue starts */ + this.refs.gridControl.refs.userControl.setState({ activePlayer: 1 }); - /** - * START - * - * @param payload - */ - makeGameStart(payload) { - /** every time the blue starts */ - this.refs.gridControl.refs.userControl.setState({activePlayer: 1}); + /** Set up player names w/ server data */ + this.refs.gridControl.refs.userControl.refs.red.setState({ + name: '' !== payload.users.red ? payload.users.red : payload.users.redAnon, + }); - /** Set up player names w/ server data */ - this.refs.gridControl.refs.userControl.refs.red.setState({ - name: payload.users.red !== '' ? payload.users.red : payload.users.redAnon, + this.refs.gridControl.refs.userControl.refs.blue.setState({ + name: '' !== payload.users.blue ? payload.users.blue : payload.users.blueAnon, + desc: 'blue' === this.refs.gridControl.state.webPlayer + ? this.refs.gridControl.state.desc.you + : this.refs.gridControl.state.desc.buddy, + active: true, + }); + + this.refs.gridControl.setState({ overlay: false }); + } + + /** + * THE END + * + * @param bluePoints + * @param redPoints + * @param resign + */ + makeGameEndIfItEnds(bluePoints, redPoints, resign = false) { + let redWins = 25 < redPoints, + blueWins = 25 < bluePoints; + + if (redWins || blueWins || resign) { + this.refs.gridControl.state.sound.won.play(); + + if (false === resign) { + this.refs.gridControl.setState({ + overlay: true, + overlayTitle: (redWins ? 'Red' : 'Blue') + ' wins the game!', + overlaySubTitle: 'Play again!', }); + } - this.refs.gridControl.refs.userControl.refs.blue.setState({ - name: payload.users.blue !== '' ? payload.users.blue : payload.users.blueAnon, - desc: this.refs.gridControl.state.webPlayer === 'blue' - ? this.refs.gridControl.state.desc.you - : this.refs.gridControl.state.desc.buddy, - active: true, - }); + this.refs.gridControl.showLeftMines(); - this.refs.gridControl.setState({overlay: false}); + this.refs.gridControl.refs.userControl.setState({ activePlayer: false }); + this.refs.gridControl.refs.userControl.refs.red.setState({ desc: '' }); + this.refs.gridControl.refs.userControl.refs.blue.setState({ desc: '' }); } + } + + resignProcess(color) { + this.refs.gridControl.setState({ + overlay: true, + overlayTitle: color === this.refs.gridControl.state.webPlayer + ? 'You have been give up' + : 'Your opponent has been resigned', + overlaySubTitle: color === this.refs.gridControl.state.webPlayer + ? 'You LOSE!' + : 'You WIN!', + }); + + this.setState({ end: true }); + + this.makeGameEndIfItEnds(0, 0, true); + } + + clickResign() { + /** PUBLISH */ + this.state.session.publish(this.state.channel, { + 'resign': this.refs.gridControl.refs.userControl.state.activePlayer ? 'blue' : 'red', + }); + this.resignProcess(this.refs.gridControl.state.webPlayer); + } + + clickResignCancel() { + this.refs.gridControl.setState({ + overlay: false, + }); + } + + /** RESIGN */ + resign() { + let users = this.refs.gridControl.refs.userControl, + activePlayer = users.state.activePlayer ? 'blue' : 'red'; + + if (this.refs.gridControl.state.webPlayer === activePlayer) { + this.refs.gridControl.setState({ + overlay: true, + overlayTitle: 'Are u sure u want to resign?!', + overlaySubTitle: ( +
+ Yes + No! +
+ ), + }); + } + } + + /** + * @see https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus + * @see https://developers.facebook.com/docs/sharing/reference/send-dialog + * @see https://developers.facebook.com/docs/plugins/share-button/ + */ + clickFBShare() { + let display = 'popup'; + + FB.getLoginStatus(function(response) { + display = 'connected' === response.status + ? 'dialog' + : 'popup'; + }); + + FB.ui({ + method: 'send', + display: display, + link: window.location.href + '/' + this.state.gameAssoc, + }); + } + + wInit(session, gridServer, gridClient) { + this.setState({ session: session }); + + /** save session to GridControl */ + /** render grid fields - @see #12 */ + this.refs.gridControl.setState({ + grid: this.state.gameInherited ? gridServer : gridClient, + channel: this.state.channel, + desc: { + buddy: ( +
+ Your buddy is
+ making a
+ move. +
+ ), + you: ( +
+ It is your turn!
+ Make a move. +
+ ), + }, + overlay: true, + overlayTitle: 'We are waiting for your opponent...', + overlaySubTitle: this.state.gameAssoc + ? ( +
+

Share this unique link w/ your opponent

+
+ +
+ {'dev' !== this.state.env + && Share in Facebook message + } + {'dev' === this.state.env + && Play w/ me! + } +
+ ) : '', + renderGridFields: this.state.gameAssoc, + }); + } + + wSubscribe(payload, rpcUsers = null) { + 'dev' === this.state.env && console.info( + ('undefined' !== typeof payload.user ? payload.user : 'user') + ' has been subscribed to the channel!', + ); + + let firstUser = !rpcUsers; + + null === this.refs.gridControl.state.webPlayer && this.refs.gridControl.setState({ + webPlayer: payload.user === payload.users.blue + || ( + firstUser && '' !== payload.users.blueAnon + || !firstUser && ('' === rpcUsers.blueAnon && '' === rpcUsers.blue) + ) + ? 'blue' : 'red', + }); + + /** rwd */ + (900 > $(document).width()) && this.currectGridSize(); + + /** every user has been came */ + if ( + 2 === payload.userCnt + && ( + !this.state.connectionLost + || this.state.connectionLost && false === this.refs.gridControl.refs.userControl.state.activePlayer && !this.state.end + ) + ) { + this.makeGameStart(payload); + } + } + + wUnsubscribe(payload) { + 'dev' === this.state.env && console.info(payload.msg); + + this.refs.gridControl.setState({ + overlay: true, + overlayTitle: 'The connection has been lost w/ your friend...', + overlaySubTitle: 'Please, restart the game!', + }); + } + + wTopic(payload) { + /** Auto-Step if this player is not the current user */ + if (this.refs.gridControl.state.webPlayer !== payload.data.player) { + if (null === payload.data.resign) { + 'dev' === this.state.env && console.warn(payload.user + ' has been stepped to coords: ' + payload.data.coords[0] + ', ' + payload.data.coords[1]); + 'dev' === this.state.env && console.warn('Opponent stepped: Auto-Step process'); + + this.refs.gridControl.refs.userControl.setState({ bombSelected: payload.data.bomb }); + + /** STEP */ + let points = this.makePointsCalcAndStep(payload.data.coords); + + /** THE END */ + this.makeGameEndIfItEnds(points.blue, points.red); + } else { + /** RESIGN */ + /** THE END */ + this.resignProcess(payload.data.resign); + } + } + } + + /** Connect - Subscribe */ + subscribe(rpcUsers = null) { + this.state.session.subscribe( + this.state.channel, + (uri, payload) => { + let isTopicEvent = 'undefined' !== typeof payload.data, + isNotUnsubscribe = 'undefined' === typeof payload.msg; + + /** CONNECTION */ + if (isTopicEvent) { + this.wTopic(payload); + } else { + if (isNotUnsubscribe) { + this.wSubscribe(payload, rpcUsers); + } else { + this.wUnsubscribe(payload); + } + } + + /** RECONNECTION */ + if (2 === payload.userCnt && this.state.connectionLost) { + 'dev' === this.state.env && console.info('Reconnection process'); + + /** PUBLISH */ + let cache = this.state.stepCache; + cache.forEach(item => this.state.session.publish(this.state.channel, item)); + this.setState({ connectionLost: false, stepCache: [] }); + } + }); + } + + connectWithWebsocket() { + /** Create Websocket w/ Bahnhof.js */ + let websocket = WS.connect( + ('true' === this.state.ssl ? 'wss' : 'ws') + '://' + window.location.hostname + '/ws/', + ); /** - * THE END - * - * @param bluePoints - * @param redPoints - * @param resign + * Connect + * Session is an Autobahn JS WAMP session. */ - makeGameEndIfItEnds(bluePoints, redPoints, resign = false) { - let redWins = redPoints > 25, - blueWins = bluePoints > 25; + websocket.on('socket/connect', session => { + 'dev' === this.state.env && console.info('Successfully connected to the Server!'); - if (redWins || blueWins || resign) { - this.refs.gridControl.state.sound.won.play(); + if (!this.state.connectionLost) { + let gridClient = this.state.gameInherited || new Grid().state.grid; - if (false === resign) { + /** + * Connect - RPC + * Send grid information to the server + */ + session + .call( + this.state.gameInherited ? 'mineseeker-rpc/connectGame' : 'mineseeker-rpc/startGame', + this.state.gameInherited ? this.state.gameAssoc : [window.btoa(JSON.stringify(gridClient)), this.state.gameAssoc], + ) + .then( + data => { + 'dev' === this.state.env && console.info('RPC has been called'); + + let serverData = true !== data[0] + ? JSON.parse(window.atob(data)) + : data; + + /** Check the grid if the user is inherited @see #30 */ + if ((this.state.gameInherited && 'undefined' !== typeof serverData.grid) || !this.state.gameInherited) { + this.wInit(session, serverData.grid, gridClient); + this.subscribe(this.state.gameInherited && serverData.users); + } else { this.refs.gridControl.setState({ - overlay: true, - overlayTitle: (redWins ? 'Red' : 'Blue') + " wins the game!", - overlaySubTitle: "Play again!" + overlay: true, + overlayTitle: 'This channel does not exists!', + overlaySubTitle: Restart game!, }); - } - this.refs.gridControl.showLeftMines(); - - this.refs.gridControl.refs.userControl.setState({activePlayer: false}); - this.refs.gridControl.refs.userControl.refs.red.setState({desc: ""}); - this.refs.gridControl.refs.userControl.refs.blue.setState({desc: ""}); - } - } - - resignProcess(color) { - this.refs.gridControl.setState({ - overlay: true, - overlayTitle: color === this.refs.gridControl.state.webPlayer - ? "You have been give up" - : "Your opponent has been resigned", - overlaySubTitle: color === this.refs.gridControl.state.webPlayer - ? "You LOSE!" - : "You WIN!" - }); - - this.setState({end: true}); - - this.makeGameEndIfItEnds(0, 0, true); - } - - clickResign() { - /** PUBLISH */ - this.state.session.publish(this.state.channel, { - 'resign': this.refs.gridControl.refs.userControl.state.activePlayer ? 'blue' : 'red' - }); - this.resignProcess(this.refs.gridControl.state.webPlayer); - } - - clickResignCancel() { - this.refs.gridControl.setState({ - overlay: false - }); - } - - /** RESIGN */ - resign() { - let users = this.refs.gridControl.refs.userControl, - activePlayer = users.state.activePlayer ? 'blue' : 'red'; - - if (this.refs.gridControl.state.webPlayer === activePlayer) { - this.refs.gridControl.setState({ - overlay: true, - overlayTitle: "Are u sure u want to resign?!", - overlaySubTitle:
- Yes - No! -
- }); - } - } - - /** - * @see https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus - * @see https://developers.facebook.com/docs/sharing/reference/send-dialog - * @see https://developers.facebook.com/docs/plugins/share-button/ - */ - clickFBShare() { - let display = 'popup'; - - FB.getLoginStatus(function (response) { - display = response.status === 'connected' - ? 'dialog' - : 'popup'; - }); - - FB.ui({ - method: 'send', - display: display, - link: window.location.href + '/' + this.state.gameAssoc, - }); - } - - wInit(session, gridServer, gridClient) { - this.setState({session: session}); - - /** save session to GridControl */ - /** render grid fields - @see #12 */ - this.refs.gridControl.setState({ - grid: this.state.gameInherited ? gridServer : gridClient, - channel: this.state.channel, - desc: { - buddy:
- Your buddy is
- making a
- move. -
, - you:
- It is your turn!
- Make a move. -
+ console.error('This channel does not exists!'); + } }, - overlay: true, - overlayTitle: "We are waiting for your opponent...", - overlaySubTitle: this.state.gameAssoc - ? -
-

Share this unique link w/ your opponent

-
- -
- {this.state.env !== 'dev' && - Share in Facebook message - } - {this.state.env === 'dev' && - Play w/ me! - } -
- : '', - renderGridFields: this.state.gameAssoc - }); - } - - wSubscribe(payload, rpcUsers = null) { - this.state.env === 'dev' && console.info( - (typeof payload.user !== 'undefined' ? payload.user : 'user') + " has been subscribed to the channel!" - ); - - let firstUser = !rpcUsers; - - this.refs.gridControl.state.webPlayer === null && this.refs.gridControl.setState({ - webPlayer: payload.user === payload.users.blue || - ( - firstUser && payload.users.blueAnon !== '' || - !firstUser && (rpcUsers.blueAnon === '' && rpcUsers.blue === '') - ) - ? 'blue' : 'red' - }); - - /** rwd */ - (900 > $(document).width()) && this.currectGridSize(); - - /** every user has been came */ - if ( - payload.userCnt === 2 && - ( - !this.state.connectionLost || - this.state.connectionLost && false === this.refs.gridControl.refs.userControl.state.activePlayer && !this.state.end - ) - ) { - this.makeGameStart(payload); - } - } - - wUnsubscribe(payload) { - this.state.env === 'dev' && console.info(payload.msg); - - this.refs.gridControl.setState({ - overlay: true, - overlayTitle: "The connection has been lost w/ your friend...", - overlaySubTitle: "Please, restart the game!" - }); - } - - wTopic(payload) { - /** Auto-Step if this player is not the current user */ - if (this.refs.gridControl.state.webPlayer !== payload.data.player) { - if (null === payload.data.resign) { - this.state.env === 'dev' && console.warn(payload.user + " has been stepped to coords: " + payload.data.coords[0] + ', ' + payload.data.coords[1]); - this.state.env === 'dev' && console.warn('Opponent stepped: Auto-Step process'); - - this.refs.gridControl.refs.userControl.setState({bombSelected: payload.data.bomb}); - - /** STEP */ - let points = this.makePointsCalcAndStep(payload.data.coords); - - /** THE END */ - this.makeGameEndIfItEnds(points.blue, points.red); - } else { - /** RESIGN */ - /** THE END */ - this.resignProcess(payload.data.resign); - } - } - } - - /** Connect - Subscribe */ - subscribe(rpcUsers = null) { - this.state.session.subscribe( - this.state.channel, - (uri, payload, log) => { - let isTopicEvent = typeof payload.data !== 'undefined', - isNotUnsubscribe = typeof payload.msg === 'undefined'; - - /** CONNECTION */ - if (isTopicEvent) { - this.wTopic(payload); - } else { - if (isNotUnsubscribe) { - this.wSubscribe(payload, rpcUsers); - } else { - this.wUnsubscribe(payload); - } - } - - /** RECONNECTION */ - if (payload.userCnt === 2 && this.state.connectionLost) { - this.state.env === 'dev' && console.info('Reconnection process'); - - /** PUBLISH */ - let cache = this.state.stepCache; - cache.forEach((item) => this.state.session.publish(this.state.channel, item)); - this.setState({connectionLost: false, stepCache: []}); - } - }); - } - - connectWithWebsocket() { - /** Create Websocket w/ Bahnhof.js */ - let websocket = WS.connect( - (this.state.ssl === 'true' ? "wss" : "ws") + "://" + window.location.hostname + "/ws/" - ); - - /** - * Connect - * Session is an Autobahn JS WAMP session. - */ - websocket.on("socket/connect", (session) => { - this.state.env === 'dev' && console.info("Successfully connected to the Server!"); - - if (!this.state.connectionLost) { - let gridClient = this.state.gameInherited || new Grid().state.grid; - - /** - * Connect - RPC - * Send grid information to the server - */ - session - .call( - this.state.gameInherited ? "mineseeker-rpc/connectGame" : "mineseeker-rpc/startGame", - this.state.gameInherited ? this.state.gameAssoc : [window.btoa(JSON.stringify(gridClient)), this.state.gameAssoc] - ) - .then( - (data) => { - this.state.env === 'dev' && console.info('RPC has been called'); - - let serverData = data[0] !== true - ? JSON.parse(window.atob(data)) - : data; - - /** Check the grid if the user is inherited @see #30 */ - if ((this.state.gameInherited && typeof serverData.grid !== 'undefined') || !this.state.gameInherited) { - this.wInit(session, serverData.grid, gridClient); - this.subscribe(this.state.gameInherited && serverData.users); - } else { - this.refs.gridControl.setState({ - overlay: true, - overlayTitle: "This channel does not exists!", - overlaySubTitle: Restart game! - }); - - console.error("This channel does not exists!"); - } - }, - (error, desc) => this.state.env === 'dev' && console.error(["RPC Error", error, desc]) - ); - } else { - this.setState({session: session}); - this.subscribe(); - } - }); - - /** - * DisConnect - * Error provides us with some insight into the disconnection: error.reason and error.code - */ - websocket.on("socket/disconnect", (error) => { - this.state.env === 'dev' && console.error("Disconnected for " + error.reason + " with code " + error.code); - - error.code === 6 && this.setState({connectionLost: true}); - error.code === 3 && setTimeout(this.componentDidMount.bind(this), 500); - }); - } - - /** After rendering */ - componentDidMount() { - this.connectWithWebsocket(); - } + (error, desc) => 'dev' === this.state.env && console.error(['RPC Error', error, desc]), + ); + } else { + this.setState({ session: session }); + this.subscribe(); + } + }); /** - * Cache the steps unless reconnection - * - * @param dataPack + * DisConnect + * Error provides us with some insight into the disconnection: error.reason and error.code */ - cachePublish(dataPack) { - let cache = this.state.stepCache; - cache.push(dataPack); - this.setState({stepCache: cache}); + websocket.on('socket/disconnect', error => { + 'dev' === this.state.env && console.error('Disconnected for ' + error.reason + ' with code ' + error.code); + + 6 === error.code && this.setState({ connectionLost: true }); + 3 === error.code && setTimeout(this.componentDidMount.bind(this), 500); + }); + } + + /** After rendering */ + componentDidMount() { + this.connectWithWebsocket(); + } + + /** + * Cache the steps unless reconnection + * + * @param dataPack + */ + cachePublish(dataPack) { + let cache = this.state.stepCache; + cache.push(dataPack); + this.setState({ stepCache: cache }); + } + + onClick(coords) { + let activePlayer = this.refs.gridControl.refs.userControl.state.activePlayer ? 'blue' : 'red'; + + /** if the clicked field is NEVER CLICKED */ + if (this.refs.gridControl.checkFieldHasBeenNeverClicked(coords[0], coords[1])) { + /** Player step and it is the current player */ + if (activePlayer === this.refs.gridControl.state.webPlayer) { + /** STEP */ + let points = this.makePointsCalcAndStep(coords); + + /** THE END */ + this.makeGameEndIfItEnds(points.blue, points.red); + + let dataPack = { + 'coords': coords, + 'player': activePlayer, + 'bomb': this.refs.gridControl.refs.userControl.state.bombSelected, + 'redPoints': points.red, + 'bluePoints': points.blue, + 'resign': null, + 'redExplodedBomb': 'red' === activePlayer && this.refs.gridControl.refs.userControl.state.bombSelected, + 'blueExplodedBomb': 'blue' === activePlayer && this.refs.gridControl.refs.userControl.state.bombSelected, + }; + + /** PUBLISH */ + !this.state.connectionLost + ? this.state.session.publish(this.state.channel, dataPack) + : this.cachePublish(dataPack); + } } + } - onClick(coords) { - let activePlayer = this.refs.gridControl.refs.userControl.state.activePlayer ? 'blue' : 'red'; - - /** if the clicked field is NEVER CLICKED */ - if (this.refs.gridControl.checkFieldHasBeenNeverClicked(coords[0], coords[1])) { - /** Player step and it is the current player */ - if (activePlayer === this.refs.gridControl.state.webPlayer) { - /** STEP */ - let points = this.makePointsCalcAndStep(coords); - - /** THE END */ - this.makeGameEndIfItEnds(points.blue, points.red); - - let dataPack = { - 'coords': coords, - 'player': activePlayer, - 'bomb': this.refs.gridControl.refs.userControl.state.bombSelected, - 'redPoints': points.red, - 'bluePoints': points.blue, - 'resign': null, - 'redExplodedBomb': activePlayer === 'red' && this.refs.gridControl.refs.userControl.state.bombSelected, - 'blueExplodedBomb': activePlayer === 'blue' && this.refs.gridControl.refs.userControl.state.bombSelected - }; - - /** PUBLISH */ - !this.state.connectionLost - ? this.state.session.publish(this.state.channel, dataPack) - : this.cachePublish(dataPack); - } - } - } - - render() { - return ( - - ); - } + render() { + return ( + + ); + } } export default MineSeeker; diff --git a/assets/js/mine-seeker/grid/grid-control.js b/assets/js/mine-seeker/grid/grid-control.js index 1a0dc62..3ad8677 100644 --- a/assets/js/mine-seeker/grid/grid-control.js +++ b/assets/js/mine-seeker/grid/grid-control.js @@ -1,445 +1,447 @@ import React from 'react'; import GridField from './grid-field'; import UserControl from '../user/user-control'; -import {Howl, Howler} from 'howler'; +import { Howl } from 'howler'; class GridControl extends React.Component { - constructor(props) { - super(props); + constructor(props) { + super(props); - let click = new Howl({src: ['/sound/click.mp3']}), - bomb = new Howl({src: ['/sound/bomb.mp3']}), - mine = new Howl({src: ['/sound/mine.mp3']}), - warning = new Howl({src: ['/sound/warning.mp3']}), - won = new Howl({src: ['/sound/won.mp3']}); + let click = new Howl({ src: ['/sound/click.mp3'] }), + bomb = new Howl({ src: ['/sound/bomb.mp3'] }), + mine = new Howl({ src: ['/sound/mine.mp3'] }), + warning = new Howl({ src: ['/sound/warning.mp3'] }), + won = new Howl({ src: ['/sound/won.mp3'] }); - this.state = { - env: props.env, - webPlayer: null, - grid: null, - desc: null, - renderGridFields: false, - gridFields: [], - updatedFieldCache: [], - bombFieldCache: [], - foundUserMineCache: 0, - playBomb: false, - overlay: false, - overlayTitle: "", - overlaySubTitle: "", - sound: { - click: click, - bomb: bomb, - mine: mine, - warning: warning, - won: won - }, - lastClicked: { - red: null, - blue: null - } - }; + this.state = { + env: props.env, + webPlayer: null, + grid: null, + desc: null, + renderGridFields: false, + gridFields: [], + updatedFieldCache: [], + bombFieldCache: [], + foundUserMineCache: 0, + playBomb: false, + overlay: false, + overlayTitle: '', + overlaySubTitle: '', + sound: { + click: click, + bomb: bomb, + mine: mine, + warning: warning, + won: won, + }, + lastClicked: { + red: null, + blue: null, + }, + }; + } + + refString(row, col) { + return 'gridField_' + row + '_' + col; + } + + checkMine(row, col) { + return 'undefined' !== typeof this.state.grid[row] && 'undefined' !== typeof this.state.grid[row][col] && 'm' !== this.state.grid[row][col]; + } + + checkFieldHasBeenNeverClicked(row, col) { + return 0 > this.state.updatedFieldCache.indexOf(this.refString(row, col)) && !this.refs[this.refString(row, col)].state.active; + } + + getBombRadius(row, col) { + let isBombTargetCenter = 1 < row && row < this.state.grid.length - 2 && 1 < col && col < this.state.grid[row].length - 2; + + /** if the (5x5) target not fits the grid */ + if (!isBombTargetCenter) { + col = 2 > col ? 2 : col; + row = 2 > row ? 2 : row; + row = row > this.state.grid.length - 3 ? this.state.grid.length - 3 : row; + col = col > this.state.grid[0].length - 3 ? this.state.grid[0].length - 3 : col; } - refString(row, col) { - return 'gridField_' + row + '_' + col; - } + return [ + [row, col], [row - 2, col - 2], [row - 2, col], [row - 2, col + 2], [row, col - 2], [row, col + 2], + [row + 2, col - 2], [row + 2, col], [row + 2, col + 2], [row - 2, col + 1], [row - 2, col - 1], + [row - 1, col - 2], [row - 1, col - 1], [row - 1, col], [row - 1, col + 1], [row - 1, col + 2], + [row, col - 1], [row, col + 1], [row + 1, col - 2], [row + 1, col - 1], [row + 1, col], + [row + 1, col + 1], [row + 1, col + 2], [row + 2, col - 1], [row + 2, col + 1], + ]; + } - checkMine(row, col) { - return typeof this.state.grid[row] !== 'undefined' && typeof this.state.grid[row][col] !== 'undefined' && this.state.grid[row][col] !== 'm'; - } + getBombFieldRadius() { + return [ + [null, null], [0, 0], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2], [2, 2], + [null, null], [null, null], [null, null], [null, null], [null, null], [null, null], [null, null], + [null, null], [null, null], [null, null], [null, null], [null, null], [null, null], [null, null], + [null, null], [null, null], + ]; + } - checkFieldHasBeenNeverClicked(row, col) { - return this.state.updatedFieldCache.indexOf(this.refString(row, col)) < 0 && !this.refs[this.refString(row, col)].state.active; - } + getNeighbourRadius(row, col) { + return [ + [row - 1, col], [row - 1, col - 1], [row - 1, col + 1], [row, col - 1], [row, col + 1], [row + 1, col], + [row + 1, col + 1], [row + 1, col - 1], + ]; + } - getBombRadius(row, col) { - let isBombTargetCenter = row > 1 && row < this.state.grid.length - 2 && col > 1 && col < this.state.grid[row].length - 2; + checkNeighbourItem(row, col) { + if (this.checkMine(row, col)) { + var currentField = this.refs[this.refString(row, col)]; - /** if the (5x5) target not fits the grid */ - if (!isBombTargetCenter) { - col = col < 2 ? 2 : col; - row = row < 2 ? 2 : row; - row = row > this.state.grid.length - 3 ? this.state.grid.length - 3 : row; - col = col > this.state.grid[0].length - 3 ? this.state.grid[0].length - 3 : col; - } + /** + * It must be cached because the GridField.state not updated until + * all showAppropriateFields() method runned out!! + */ + if (this.checkFieldHasBeenNeverClicked(row, col)) { + this.state.updatedFieldCache.push(this.refString(row, col)); - return [ - [row, col], [row - 2, col - 2], [row - 2, col], [row - 2, col + 2], [row, col - 2], [row, col + 2], - [row + 2, col - 2], [row + 2, col], [row + 2, col + 2], [row - 2, col + 1], [row - 2, col - 1], - [row - 1, col - 2], [row - 1, col - 1], [row - 1, col], [row - 1, col + 1], [row - 1, col + 2], - [row, col - 1], [row, col + 1], [row + 1, col - 2], [row + 1, col - 1], [row + 1, col], - [row + 1, col + 1], [row + 1, col + 2], [row + 2, col - 1], [row + 2, col + 1] - ]; - } - - getBombFieldRadius() { - return [ - [null, null], [0, 0], [1, 0], [2, 0], [0, 1], [2, 1], [0, 2], [1, 2], [2, 2], - [null, null], [null, null], [null, null], [null, null], [null, null], [null, null], [null, null], - [null, null], [null, null], [null, null], [null, null], [null, null], [null, null], [null, null], - [null, null], [null, null] - ]; - } - - getNeighbourRadius(row, col) { - return [ - [row - 1, col], [row - 1, col - 1], [row - 1, col + 1], [row, col - 1], [row, col + 1], [row + 1, col], - [row + 1, col + 1], [row + 1, col - 1] - ]; - } - - checkNeighbourItem(row, col) { - if (this.checkMine(row, col)) { - var currentField = this.refs[this.refString(row, col)]; - - /** - * It must be cached because the GridField.state not updated until - * all showAppropriateFields() method runned out!! - */ - if (this.checkFieldHasBeenNeverClicked(row, col)) { - this.state.updatedFieldCache.push(this.refString(row, col)); - - currentField.setState({ - currentImage: this.state.grid[row][col], - currentObj: this.state.grid[row][col], - active: true - }); - - if (this.state.grid[row][col] === 0) { - return { - row: row, - col: col - }; - } - } - } - - return false; - } - - checkNeighbours(row, col) { - let anotherFields = [], - neighbours = this.getNeighbourRadius(row, col); - - for (let i = 0, j = neighbours.length; i < j; i++) { - anotherFields.push(this.checkNeighbourItem(neighbours[i][0], neighbours[i][1])); - } - - return anotherFields; - } - - bombClear() { - if (this.state.bombFieldCache.length) { - for (var i = 0, j = this.state.bombFieldCache.length; i < j; i++) { - var cacheItem = this.state.bombFieldCache[i]; - this.refs[this.refString(cacheItem[0], cacheItem[1])] - .setState({bombTargetArea: null}); - } - this.state.bombFieldCache = []; - } - } - - bombCreate(row, col) { - var bombFieldSymbols = this.getBombFieldRadius(), - bombFields = this.getBombRadius(row, col); - - for (var i = 0, j = bombFields.length; i < j; i++) { - this.state.bombFieldCache.push(bombFields[i]); - - this.refs[this.refString(bombFields[i][0], bombFields[i][1])] - .setState({bombTargetArea: bombFieldSymbols[i]}); - } - } - - showLeftMines() { - for (let i = 0, j = this.state.grid.length; i < j; i++) { - for (let k = 0, l = this.state.grid[i].length; k < l; k++) { - let currentField = this.refs[this.refString(i, k)]; - - if (this.state.grid[i][k] === 'm' && this.checkFieldHasBeenNeverClicked(i, k)) { - currentField.setState({ - currentImage: currentField.state.icons.root + currentField.state.icons.left - }); - } - } - } - } - - /** set __ACTIVE__ player in the UserControl !!!! */ - changePlayer(idx, max, currentObject) { - var userControl = this.refs.userControl, - activePlayer = userControl.state.activePlayer ? 'blue' : 'red', - inactivePlayer = userControl.state.activePlayer ? 'red' : 'blue'; - - if ( - userControl.state.bombSelected && idx === (max - 1) || - !idx && !userControl.state.bombSelected && currentObject !== 'm' - ) { - userControl.setState({ - activePlayer: userControl.state.activePlayer ? 0 : 1 - }); - - /** the desc is inversely because the user.active is not changed yet !!! */ - userControl.refs[activePlayer].setState({ - active: false, - desc: "" - }); - - userControl.refs[inactivePlayer].setState({ - active: true, - desc: activePlayer === this.state.webPlayer - ? this.state.desc.buddy - : this.state.desc.you - }); - } - } - - /** - * Show all fields that needed after click - * - * @param currentField - * @param row - * @param col - */ - showAppropriateFields(currentField, row, col) { currentField.setState({ - currentObj: this.state.grid[row][col], - active: true + currentImage: this.state.grid[row][col], + currentObj: this.state.grid[row][col], + active: true, }); - if (this.checkFieldHasBeenNeverClicked(row, col)) { - this.state.updatedFieldCache.push(this.refString(row, col)); - } - - if (this.state.grid[row][col] === 0) { - var neighbours = this.checkNeighbours(row, col); - - neighbours - .filter((i) => { - return i !== false; - }) - .forEach((element, index, array) => { - var currentField = this.refs[this.refString(element.row, element.col)]; - this.showAppropriateFields(currentField, element.row, element.col); - }); + if (0 === this.state.grid[row][col]) { + return { + row: row, + col: col, + }; } + } } - /** - * Player control method - * - * @param currentObject {int|string} Current object from Grid class - * @param row {int} - * @param col {int} - * @param justOnFirstIteration {int} When bomb is being used check the whole explosion area - */ - handleGridField(currentObject, row, col, justOnFirstIteration = 0) { - var userControl = this.refs.userControl, - gridFieldControl = this.refs[this.refString(row, col)], - activePlayer = userControl.state.activePlayer ? 'blue' : 'red', - inactivePlayer = userControl.state.activePlayer ? 'red' : 'blue'; + return false; + } - /** if the clicked field is NEVER CLICKED */ - if (this.checkFieldHasBeenNeverClicked(row, col)) { - /** update LAST CLICKED grid field */ - if (!justOnFirstIteration) { - if (this.state.lastClicked[activePlayer] !== null) { - this.refs[this.refString(this.state.lastClicked[activePlayer][0], this.state.lastClicked[activePlayer][1])].setState({ - lastClickedRed: false, - lastClickedBlue: false - }); - } - } + checkNeighbours(row, col) { + let anotherFields = [], + neighbours = this.getNeighbourRadius(row, col); - this.state.lastClicked[activePlayer] = [row, col]; + for (let i = 0, j = neighbours.length; i < j; i++) { + anotherFields.push(this.checkNeighbourItem(neighbours[i][0], neighbours[i][1])); + } - /** if you found mine */ - if (currentObject === 'm') { - this.state.foundUserMineCache++; + return anotherFields; + } - if (!justOnFirstIteration) { - /** set last clicked field w/ color */ - this.state.lastClicked[activePlayer] = [row, col]; + bombClear() { + if (this.state.bombFieldCache.length) { + for (var i = 0, j = this.state.bombFieldCache.length; i < j; i++) { + var cacheItem = this.state.bombFieldCache[i]; + this.refs[this.refString(cacheItem[0], cacheItem[1])] + .setState({ bombTargetArea: null }); + } + this.state.bombFieldCache = []; + } + } - this.state.sound[ - (userControl.refs[activePlayer].state.mines + this.state.foundUserMineCache) > 20 - ? 'warning' - : 'mine' - ].play(); - } + bombCreate(row, col) { + var bombFieldSymbols = this.getBombFieldRadius(), + bombFields = this.getBombRadius(row, col); - /** set current image in field */ - gridFieldControl.setState({ - currentImage: gridFieldControl.state.icons.root + gridFieldControl.state.icons.flag[activePlayer] - }); - } else { - this.state.sound.click.play(); + for (var i = 0, j = bombFields.length; i < j; i++) { + this.state.bombFieldCache.push(bombFields[i]); - /** set current image in field - WHEN it is a number */ - if (!isNaN(currentObject)) { - gridFieldControl.setState({ - currentImage: currentObject - }); - } - } + this.refs[this.refString(bombFields[i][0], bombFields[i][1])] + .setState({ bombTargetArea: bombFieldSymbols[i] }); + } + } - /** - * set bombs status - we must add one mine (currentObject === 'm' ? 1 : 0) to current mine - * when it found NOW because the status is not refreshed unless the handleGridField() ends - */ - userControl.refs[activePlayer].setState({ - enabledBomb: userControl.refs[activePlayer].state.mines + (currentObject === 'm' ? 1 : 0) <= userControl.refs[inactivePlayer].state.mines - }); + showLeftMines() { + for (let i = 0, j = this.state.grid.length; i < j; i++) { + for (let k = 0, l = this.state.grid[i].length; k < l; k++) { + let currentField = this.refs[this.refString(i, k)]; - userControl.refs[inactivePlayer].setState({ - enabledBomb: userControl.refs[activePlayer].state.mines + (currentObject === 'm' ? 1 : 0) >= userControl.refs[inactivePlayer].state.mines - }); - - /** set-up last clicked */ - if (!justOnFirstIteration) { - gridFieldControl.setState({ - lastClickedRed: activePlayer === 'red', - lastClickedBlue: activePlayer === 'blue' - }); - } + if ('m' === this.state.grid[i][k] && this.checkFieldHasBeenNeverClicked(i, k)) { + currentField.setState({ + currentImage: currentField.state.icons.root + currentField.state.icons.left, + }); } + } + } + } + + /** set __ACTIVE__ player in the UserControl !!!! */ + changePlayer(idx, max, currentObject) { + var userControl = this.refs.userControl, + activePlayer = userControl.state.activePlayer ? 'blue' : 'red', + inactivePlayer = userControl.state.activePlayer ? 'red' : 'blue'; + + if ( + userControl.state.bombSelected && idx === (max - 1) + || !idx && !userControl.state.bombSelected && 'm' !== currentObject + ) { + userControl.setState({ + activePlayer: userControl.state.activePlayer ? 0 : 1, + }); + + /** the desc is inversely because the user.active is not changed yet !!! */ + userControl.refs[activePlayer].setState({ + active: false, + desc: '', + }); + + userControl.refs[inactivePlayer].setState({ + active: true, + desc: activePlayer === this.state.webPlayer + ? this.state.desc.buddy + : this.state.desc.you, + }); + } + } + + /** + * Show all fields that needed after click + * + * @param currentField + * @param row + * @param col + */ + showAppropriateFields(currentField, row, col) { + currentField.setState({ + currentObj: this.state.grid[row][col], + active: true, + }); + + if (this.checkFieldHasBeenNeverClicked(row, col)) { + this.state.updatedFieldCache.push(this.refString(row, col)); } - /** - * Show elems w/ conditions - * - * @param row - * @param col - * @param idx - * @param max - */ - show(row, col, idx = 0, max = 0) { - this.handleGridField(this.state.grid[row][col], row, col, idx); - this.showAppropriateFields(this.refs[this.refString(row, col)], row, col); - this.changePlayer(idx, max, this.state.grid[row][col]); + if (0 === this.state.grid[row][col]) { + let neighbours = this.checkNeighbours(row, col); + + neighbours + .filter(i => false !== i) + .forEach(element => { + let currentField = this.refs[this.refString(element.row, element.col)]; + this.showAppropriateFields(currentField, element.row, element.col); + }); } + } - /** - * STEP one - * - * @param coords - */ - stepEvent(coords) { - /** if the clicked field is NEVER CLICKED */ - if (this.checkFieldHasBeenNeverClicked(coords[0], coords[1])) { - var activePlayer = this.refs.userControl.state.activePlayer ? 'blue' : 'red'; + /** + * Player control method + * + * @param currentObject {int|string} Current object from Grid class + * @param row {int} + * @param col {int} + * @param justOnFirstIteration {int} When bomb is being used check the whole explosion area + */ + handleGridField(currentObject, row, col, justOnFirstIteration = 0) { + var userControl = this.refs.userControl, + gridFieldControl = this.refs[this.refString(row, col)], + activePlayer = userControl.state.activePlayer ? 'blue' : 'red', + inactivePlayer = userControl.state.activePlayer ? 'red' : 'blue'; - this.state.foundUserMineCache = 0; - this.state.playBomb = true; - - /** Show elements */ - if (this.refs.userControl.state.bombSelected) { - this.state.sound.bomb.play(); - - var bombRadius = this.getBombRadius(coords[0], coords[1]); - for (var i = 0, j = bombRadius.length; i < j; i++) { - this.show(bombRadius[i][0], bombRadius[i][1], i, j); - } - - /** remove BOMB from activePlayer */ - this.refs.userControl.refs[activePlayer].setState({ - haveBomb: false - }); - } else { - this.show(coords[0], coords[1]); - } - - /** Mine score handling */ - if (this.state.foundUserMineCache) { - this.refs.userControl.setState({ - mines: this.refs.userControl.state.mines - this.state.foundUserMineCache, - foundMines: true - }, () => { - /** because of CSS animation in .found-mine */ - setTimeout(() => this.refs.userControl.setState({foundMines: false}), 500); - - /** add the found mines to the active Player */ - this.refs.userControl.refs[activePlayer].setState({ - mines: this.refs.userControl.refs[activePlayer].state.mines + this.state.foundUserMineCache - }); - }); - } - - /** Reset BOMB status */ - if (this.refs.userControl.state.bombSelected) { - /** reset bomb selected status */ - this.refs.userControl.setState({bombSelected: false}); - - /** clear cache, reset symbols */ - this.bombClear(); - } + /** if the clicked field is NEVER CLICKED */ + if (this.checkFieldHasBeenNeverClicked(row, col)) { + /** update LAST CLICKED grid field */ + if (!justOnFirstIteration) { + if (null !== this.state.lastClicked[activePlayer]) { + this.refs[this.refString(this.state.lastClicked[activePlayer][0], this.state.lastClicked[activePlayer][1])].setState({ + lastClickedRed: false, + lastClickedBlue: false, + }); } - } + } - /** - * On Hover when you want to drop BOMB - * Target grid field - * @param coords - */ - onHoverGridField(coords) { - if (this.refs.userControl.state.bombSelected) { - var activePlayer = this.refs.userControl.state.activePlayer ? 'blue' : 'red'; + this.state.lastClicked[activePlayer] = [row, col]; - if (activePlayer === this.state.webPlayer) { - /** clear cache, reset symbols */ - this.bombClear(); + /** if you found mine */ + if ('m' === currentObject) { + this.state.foundUserMineCache++; - /** new cache && field activate */ - this.bombCreate(coords[0], coords[1]); - } else { - this.refs.userControl.setState({bombSelected: false}); - } + if (!justOnFirstIteration) { + /** set last clicked field w/ color */ + this.state.lastClicked[activePlayer] = [row, col]; + + this.state.sound[ + 20 < (userControl.refs[activePlayer].state.mines + this.state.foundUserMineCache) + ? 'warning' + : 'mine' + ].play(); } - } - renderGridFields() { - for (let i = 0, j = this.state.grid.length; i < j; i++) { - for (let k = 0, l = this.state.grid[i].length; k < l; k++) { - this.state.gridFields.push( - - ); - } + /** set current image in field */ + gridFieldControl.setState({ + currentImage: gridFieldControl.state.icons.root + gridFieldControl.state.icons.flag[activePlayer], + }); + } else { + this.state.sound.click.play(); + + /** set current image in field - WHEN it is a number */ + if (!isNaN(currentObject)) { + gridFieldControl.setState({ + currentImage: currentObject, + }); } + } + + /** + * set bombs status - we must add one mine (currentObject === 'm' ? 1 : 0) to current mine + * when it found NOW because the status is not refreshed unless the handleGridField() ends + */ + userControl.refs[activePlayer].setState({ + enabledBomb: userControl.refs[activePlayer].state.mines + ('m' === currentObject ? 1 : 0) <= userControl.refs[inactivePlayer].state.mines, + }); + + userControl.refs[inactivePlayer].setState({ + enabledBomb: userControl.refs[activePlayer].state.mines + ('m' === currentObject ? 1 : 0) >= userControl.refs[inactivePlayer].state.mines, + }); + + /** set-up last clicked */ + if (!justOnFirstIteration) { + gridFieldControl.setState({ + lastClickedRed: 'red' === activePlayer, + lastClickedBlue: 'blue' === activePlayer, + }); + } } + } - render() { - /** Render the grid fields just one time in one party #12 */ - this.state.renderGridFields && this.renderGridFields(); - this.state.renderGridFields = false; + /** + * Show elems w/ conditions + * + * @param row + * @param col + * @param idx + * @param max + */ + show(row, col, idx = 0, max = 0) { + this.handleGridField(this.state.grid[row][col], row, col, idx); + this.showAppropriateFields(this.refs[this.refString(row, col)], row, col); + this.changePlayer(idx, max, this.state.grid[row][col]); + } - return ( -
-
-
-

{this.state.overlayTitle}

-

{this.state.overlaySubTitle}

-
-
- -
-
- <> - {this.state.gridFields} - -
-
-
+ /** + * STEP one + * + * @param coords + */ + stepEvent(coords) { + /** if the clicked field is NEVER CLICKED */ + if (this.checkFieldHasBeenNeverClicked(coords[0], coords[1])) { + var activePlayer = this.refs.userControl.state.activePlayer ? 'blue' : 'red'; + + this.state.foundUserMineCache = 0; + this.state.playBomb = true; + + /** Show elements */ + if (this.refs.userControl.state.bombSelected) { + this.state.sound.bomb.play(); + + var bombRadius = this.getBombRadius(coords[0], coords[1]); + for (var i = 0, j = bombRadius.length; i < j; i++) { + this.show(bombRadius[i][0], bombRadius[i][1], i, j); + } + + /** remove BOMB from activePlayer */ + this.refs.userControl.refs[activePlayer].setState({ + haveBomb: false, + }); + } else { + this.show(coords[0], coords[1]); + } + + /** Mine score handling */ + if (this.state.foundUserMineCache) { + this.refs.userControl.setState({ + mines: this.refs.userControl.state.mines - this.state.foundUserMineCache, + foundMines: true, + }, () => { + /** because of CSS animation in .found-mine */ + setTimeout(() => this.refs.userControl.setState({ foundMines: false }), 500); + + /** add the found mines to the active Player */ + this.refs.userControl.refs[activePlayer].setState({ + mines: this.refs.userControl.refs[activePlayer].state.mines + this.state.foundUserMineCache, + }); + }); + } + + /** Reset BOMB status */ + if (this.refs.userControl.state.bombSelected) { + /** reset bomb selected status */ + this.refs.userControl.setState({ bombSelected: false }); + + /** clear cache, reset symbols */ + this.bombClear(); + } + } + } + + /** + * On Hover when you want to drop BOMB + * Target grid field + * @param coords + */ + onHoverGridField(coords) { + if (this.refs.userControl.state.bombSelected) { + var activePlayer = this.refs.userControl.state.activePlayer ? 'blue' : 'red'; + + if (activePlayer === this.state.webPlayer) { + /** clear cache, reset symbols */ + this.bombClear(); + + /** new cache && field activate */ + this.bombCreate(coords[0], coords[1]); + } else { + this.refs.userControl.setState({ bombSelected: false }); + } + } + } + + renderGridFields() { + for (let i = 0, j = this.state.grid.length; i < j; i++) { + for (let k = 0, l = this.state.grid[i].length; k < l; k++) { + this.state.gridFields.push( + , ); + } } + } + + render() { + /** Render the grid fields just one time in one party #12 */ + this.state.renderGridFields && this.renderGridFields(); + this.state.renderGridFields = false; + + return ( +
+
+
+

{this.state.overlayTitle}

+

{this.state.overlaySubTitle}

+
+
+ +
+
+ <> + {this.state.gridFields} + +
+
+
+ ); + } } export default GridControl; diff --git a/assets/js/mine-seeker/grid/grid-field.js b/assets/js/mine-seeker/grid/grid-field.js index f649740..497e0c7 100644 --- a/assets/js/mine-seeker/grid/grid-field.js +++ b/assets/js/mine-seeker/grid/grid-field.js @@ -1,145 +1,156 @@ import React from 'react'; class GridField extends React.Component { - constructor(props) { - super(props); + constructor(props) { + super(props); - this.state = { - currentObj: 'w', - currentImage: null, - active: false, - lastClickedRed: false, - lastClickedBlue: false, - bombTargetArea: null, - icons: { - root: '/images/', - water: { - 1: 'bg-wave-1-outbg.png', - 2: 'bg-wave-1-outbg.png', - 3: 'bg-wave-2-outbg.png' - }, - flag: { - red: 'bg-flag-red-outbg.png', - blue: 'bg-flag-blue-outbg.png' - }, - target: { - lastBlue: 'bg-last-blue-outbg.png', - lastRed: 'bg-last-red-outbg.png', - crosshair: 'bg-target-outbg.png', - crosshairBomb: 'bg-target-bomb-outbg.png' - }, - left: 'bg-left-mine-outbg.png' - } - }; + this.state = { + currentObj: 'w', + currentImage: null, + active: false, + lastClickedRed: false, + lastClickedBlue: false, + bombTargetArea: null, + icons: { + root: '/images/', + water: { + 1: 'bg-wave-1-outbg.png', + 2: 'bg-wave-1-outbg.png', + 3: 'bg-wave-2-outbg.png', + }, + flag: { + red: 'bg-flag-red-outbg.png', + blue: 'bg-flag-blue-outbg.png', + }, + target: { + lastBlue: 'bg-last-blue-outbg.png', + lastRed: 'bg-last-red-outbg.png', + crosshair: 'bg-target-outbg.png', + crosshairBomb: 'bg-target-bomb-outbg.png', + }, + left: 'bg-left-mine-outbg.png', + }, + }; + } + + componentWillMount() { + var wave = Math.floor(Math.random() * 3) + 1; + + this.setState({ + currentImage: this.state.icons.root + this.state.icons.water[wave], + }); + } + + classNameWhenActive() { + return 'field' + + (true === this.state.active ? ' active' : '') + + (true === this.state.active && 'm' === this.state.currentObj ? ' mine' : '') + + ' color-' + this.state.currentObj; + } + + currentImage() { + return isNaN(this.state.currentImage) + ? ( +
+ current image +
+
+ ) : this.state.currentImage ?
{this.state.currentImage}
: ''; + } + + lastClickedClass() { + return 'field-' + + (this.state.lastClickedRed ? 'red' : '') + + (this.state.lastClickedBlue ? 'blue' : '') + '-last last-clicked'; + } + + lastClickedSrc() { + return this.state.lastClickedRed + ? '/images/bg-last-red-outbg.png' + : '/images/bg-last-blue-outbg.png'; + } + + currentLastClicked() { + return this.state.lastClickedRed || this.state.lastClickedBlue + ? ( + blue last + ) : ''; + } + + createBombTarget() { + if (null !== this.state.bombTargetArea) { + let vert, hor = ''; + + switch (this.state.bombTargetArea[0]) { + case 0: + vert = 'left'; + break; + case 1: + vert = 'center'; + break; + case 2: + vert = 'right'; + break; + default: + vert = null; + break; + } + + switch (this.state.bombTargetArea[1]) { + case 0: + hor = 'top'; + break; + case 1: + hor = 'middle'; + break; + case 2: + hor = 'bottom'; + break; + default: + vert = null; + break; + } + + var src = null === vert + ? '/images/bg-bomb-empty-outbg.png' + : '/images/bg-bomb-' + hor + '-' + vert + '-outbg.png'; + + return ( + bomb target + ); } + } - componentWillMount() { - var wave = Math.floor(Math.random() * 3) + 1; - - this.setState({ - currentImage: this.state.icons.root + this.state.icons.water[wave] - }); - } - - classNameWhenActive() { - return 'field' - + (this.state.active === true ? ' active' : '') - + (this.state.active === true && this.state.currentObj === 'm' ? ' mine' : '') - + ' color-' + this.state.currentObj; - } - - currentImage() { - return isNaN(this.state.currentImage) - ? -
- -
-
- : this.state.currentImage ?
{this.state.currentImage}
: ''; - } - - lastClickedClass() { - return 'field-' - + (this.state.lastClickedRed ? 'red' : '') - + (this.state.lastClickedBlue ? 'blue' : '') + '-last last-clicked'; - } - - lastClickedSrc() { - return this.state.lastClickedRed - ? "/images/bg-last-red-outbg.png" - : "/images/bg-last-blue-outbg.png"; - } - - currentLastClicked() { - return this.state.lastClickedRed || this.state.lastClickedBlue - ? blue last - : ''; - } - - createBombTarget() { - if (this.state.bombTargetArea !== null) { - var vert = '', hor = ''; - - switch (this.state.bombTargetArea[0]) { - case 0: - vert = 'left'; - break; - case 1: - vert = 'center'; - break; - case 2: - vert = 'right'; - break; - default: - vert = null; - break; - } - - switch (this.state.bombTargetArea[1]) { - case 0: - hor = 'top'; - break; - case 1: - hor = 'middle'; - break; - case 2: - hor = 'bottom'; - break; - default: - vert = null; - break; - } - - var src = vert === null - ? '/images/bg-bomb-empty-outbg.png' - : '/images/bg-bomb-' + hor + '-' + vert + '-outbg.png'; - - return bomb target - } - } - - render() { - return ( -
- target - {this.createBombTarget()} - {this.currentLastClicked()} -
-
- {this.currentImage()} -
-
-
- ); - } + render() { + return ( +
+ target + {this.createBombTarget()} + {this.currentLastClicked()} +
+
+ {this.currentImage()} +
+
+
+ ); + } } export default GridField; diff --git a/assets/js/mine-seeker/grid/grid.js b/assets/js/mine-seeker/grid/grid.js index 66857c7..d8306a3 100644 --- a/assets/js/mine-seeker/grid/grid.js +++ b/assets/js/mine-seeker/grid/grid.js @@ -1,101 +1,101 @@ import React from 'react'; class Grid extends React.Component { - constructor() { - super(); + constructor() { + super(); - this.state = { - row: 16, - col: 16, - mines: 51, - set: [] - }; + this.state = { + row: 16, + col: 16, + mines: 51, + set: [], + }; - this.state.grid = this.numberingGrid( - this.createGrid( - this.shuffleSet( - this.createSet( - this.state.set - ) - ) - ) - ); + this.state.grid = this.numberingGrid( + this.createGrid( + this.shuffleSet( + this.createSet( + this.state.set, + ), + ), + ), + ); + } + + createSet(obj) { + for (let i = 0, j = this.state.row * this.state.col; i < j; i++) { + obj.push( + 0 < this.state.mines + ? 'm' + : 'w', + ); + this.state.mines--; } - createSet(obj) { - for (var i = 0, j = this.state.row * this.state.col; i < j; i++) { - obj.push( - this.state.mines > 0 - ? "m" - : "w" - ); - this.state.mines--; + return obj; + } + + shuffleSet(obj) { + return obj.sort(function() { + return Math.round(Math.random()) - .5; + }); + } + + createGrid(obj) { + let grid = [[]], + row = 0, + col = 0; + + for (let i = 0, j = obj.length; i < j; i++) { + grid[row][col] = obj[i]; + + if (15 === col && 15 !== row) { + row++; + col = 0; + grid.push([]); + } else { + col++; + } + } + + return grid; + } + + checkMine(field, i, j) { + return 'undefined' !== typeof field[i] && 'undefined' !== typeof field[i][j] && 'm' === field[i][j]; + } + + isThereMine(obj, row, col) { + if (this.checkMine(obj, row, col)) { + return 1; + } + return 0; + } + + numberingGrid(obj) { + let nbr = 0; + + for (let i = 0; i < this.state.row; i++) { + for (let j = 0; j < this.state.col; j++) { + if ('w' === obj[i][j]) { + nbr = 0; + + nbr += this.isThereMine(obj, i - 1, j); + nbr += this.isThereMine(obj, i - 1, j - 1); + nbr += this.isThereMine(obj, i - 1, j + 1); + nbr += this.isThereMine(obj, i, j - 1); + nbr += this.isThereMine(obj, i, j + 1); + nbr += this.isThereMine(obj, i + 1, j); + nbr += this.isThereMine(obj, i + 1, j + 1); + nbr += this.isThereMine(obj, i + 1, j - 1); + + obj[i][j] = nbr; } - - return obj; + } } - shuffleSet(obj) { - return obj.sort(function () { - return Math.round(Math.random()) - .5; - }); - } - - createGrid(obj) { - var grid = [[]], - row = 0, - col = 0; - - for (var i = 0, j = obj.length; i < j; i++) { - grid[row][col] = obj[i]; - - if (col === 15 && row !== 15) { - row++; - col = 0; - grid.push([]); - } else { - col++; - } - } - - return grid; - } - - checkMine(field, i, j) { - return typeof field[i] !== 'undefined' && typeof field[i][j] !== 'undefined' && field[i][j] === 'm'; - } - - isThereMine(obj, row, col) { - if (this.checkMine(obj, row, col)) { - return 1; - } - return 0; - } - - numberingGrid(obj) { - var nbr = 0; - - for (var i = 0; i < this.state.row; i++) { - for (var j = 0; j < this.state.col; j++) { - if (obj[i][j] === 'w') { - nbr = 0; - - nbr += this.isThereMine(obj, i - 1, j); - nbr += this.isThereMine(obj, i - 1, j - 1); - nbr += this.isThereMine(obj, i - 1, j + 1); - nbr += this.isThereMine(obj, i, j - 1); - nbr += this.isThereMine(obj, i, j + 1); - nbr += this.isThereMine(obj, i + 1, j); - nbr += this.isThereMine(obj, i + 1, j + 1); - nbr += this.isThereMine(obj, i + 1, j - 1); - - obj[i][j] = nbr; - } - } - } - - return obj; - } + return obj; + } } export default Grid; diff --git a/assets/js/mine-seeker/user/user-control.js b/assets/js/mine-seeker/user/user-control.js index 86afcd4..17e16d9 100644 --- a/assets/js/mine-seeker/user/user-control.js +++ b/assets/js/mine-seeker/user/user-control.js @@ -2,77 +2,81 @@ import React from 'react'; import User from './user'; class UserControl extends React.Component { - constructor() { - super(); + constructor() { + super(); - /** - * activePlayer - red: 0, blue: 1 - * @type {{activePlayer: boolean, mines: number, bombSelected: boolean, foundMines: boolean}} - */ - this.state = { - activePlayer: false, - mines: 51, - bombSelected: false, - foundMines: false - }; + /** + * activePlayer - red: 0, blue: 1 + * @type {{activePlayer: boolean, mines: number, bombSelected: boolean, foundMines: boolean}} + */ + this.state = { + activePlayer: false, + mines: 51, + bombSelected: false, + foundMines: false, + }; + } + + youCanSelectBomb(activePlayer, clickedPlayer) { + return this.refs[activePlayer].state.haveBomb + && this.refs[activePlayer].state.enabledBomb + && this.state.activePlayer === clickedPlayer; + } + + onClickBombSelector(clickedPlayer) { + let activePlayer = this.state.activePlayer ? 'blue' : 'red'; + + if (this.youCanSelectBomb(activePlayer, clickedPlayer)) { + this.state.bombSelected = !this.state.bombSelected; + + if (!this.state.bombSelected) { + this.props.bombClear(); + } } + } - youCanSelectBomb(activePlayer, clickedPlayer) { - return this.refs[activePlayer].state.haveBomb && - this.refs[activePlayer].state.enabledBomb && - this.state.activePlayer === clickedPlayer; - } + getResignClass(webPlayer) { + let activePlayer = 1 === this.state.activePlayer ? 'blue' : 'red'; + return 'resign' + (webPlayer !== activePlayer ? ' disabled' : ''); + } - onClickBombSelector(clickedPlayer) { - let activePlayer = this.state.activePlayer ? 'blue' : 'red'; + activeMines() { + return 'active-mines' + (this.state.foundMines ? ' found-mine' : ''); + } - if (this.youCanSelectBomb(activePlayer, clickedPlayer)) { - this.state.bombSelected = !this.state.bombSelected; - - if (!this.state.bombSelected) { - this.props.bombClear(); - } - } - } - - getResignClass(webPlayer) { - let activePlayer = this.state.activePlayer === 1 ? 'blue' : 'red'; - return "resign" + (webPlayer !== activePlayer ? ' disabled' : ''); - } - - activeMines() { - return "active-mines" + (this.state.foundMines ? ' found-mine' : ''); - } - - render() { - return ( -
- -
- -
-
{this.state.mines}
-
-
- -
-
- - -
- ); - } + render() { + return ( +
+ +
+ +
+
{this.state.mines}
+
+
+ +
+
+ + +
+ ); + } } export default UserControl; diff --git a/assets/js/mine-seeker/user/user.js b/assets/js/mine-seeker/user/user.js index 851d082..ff93fa3 100644 --- a/assets/js/mine-seeker/user/user.js +++ b/assets/js/mine-seeker/user/user.js @@ -1,90 +1,90 @@ import React from 'react'; class User extends React.Component { - constructor(props) { - super(props); + constructor(props) { + super(props); - this.state = { - name: "...", - desc: "", - active: props.active, - color: props.color === 'blue' ? 1 : 0, - mines: 0, - srcRoot: '/images/', - haveBomb: true, - enabledBomb: true - }; + this.state = { + name: '...', + desc: '', + active: props.active, + color: 'blue' === props.color ? 1 : 0, + mines: 0, + srcRoot: '/images/', + haveBomb: true, + enabledBomb: true, + }; + } + + setColor(color) { + return 'user-container user-' + color; + } + + getSrc(color) { + return this.state.srcRoot + 'bg-flag-' + color + '-outbg.png'; + } + + getBombBuzzClass(webPlayer) { + let activePlayer = 1 === this.state.color ? 'blue' : 'red'; + + return 'bomb-container' + + ( + this.state.active && (activePlayer === webPlayer) && this.state.haveBomb && this.state.enabledBomb + ? ' buzz' + : '' + ); + } + + getBomb() { + let src = this.state.srcRoot; + + if (this.state.haveBomb) { + src += this.state.enabledBomb && this.state.active + ? 'bg-bomb-outbg.png' + : 'bg-bomb-disabled-outbg.png'; + } else { + src += 'bg-bomb-exploded-outbg.png'; } - setColor(color) { - return 'user-container user-' + color; - } + return src; + } - getSrc(color) { - return this.state.srcRoot + 'bg-flag-' + color + '-outbg.png'; - } + getFigure(color) { + return this.state.srcRoot + 'bg-figure-' + color + '-outbg.png'; + } - getBombBuzzClass(webPlayer) { - let activePlayer = this.state.color === 1 ? 'blue' : 'red'; + getCursor(state, color) { + return state + ? cursor + : ''; + } - return "bomb-container" + - ( - this.state.active && (activePlayer === webPlayer) && this.state.haveBomb && this.state.enabledBomb - ? ' buzz' - : '' - ); - } - - getBomb() { - let src = this.state.srcRoot; - - if (this.state.haveBomb) { - src += this.state.enabledBomb && this.state.active - ? 'bg-bomb-outbg.png' - : 'bg-bomb-disabled-outbg.png'; - } else { - src += 'bg-bomb-exploded-outbg.png'; - } - - return src; - } - - getFigure(color) { - return this.state.srcRoot + 'bg-figure-' + color + '-outbg.png'; - } - - getCursor(state, color) { - return state - ? cursor - : ''; - } - - render() { - return ( -
-
-
{this.props.color}
- {this.getCursor(this.props.active, this.props.color)} - figure -
-
{this.state.name}
-
-
{this.state.desc}
-
- flag -
- {this.state.mines} -
-
-
- bomb -
-
-
-
+ render() { + return ( +
+
+
{this.props.color}
+ {this.getCursor(this.props.active, this.props.color)} + figure +
+
{this.state.name}
+
+
{this.state.desc}
+
+ flag +
+ {this.state.mines} +
+
+
+ bomb
- ); - } +
+
+
+
+ ); + } } export default User;