Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
package-lock.json
38 changes: 38 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "vkapp",
"version": "0.1.0",
"description": "VK APP",
"author": "Zevako Dmitry",
"license": "Apache-2.0",
"dependencies": {
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-keyboard-event-handler": "^1.5.4",
"rest": "^1.3.1"
},
"scripts": {
"build": "--mode 'production' --config './webpack/config.js'",
"dev": "webpack-dev-server --hot --inline --config './webpack/config.js'",
"flow": "flow"
},
"devDependencies": {
"@babel/core": "^7.1.0",
"@babel/plugin-proposal-class-properties": "^7.2.1",
"@babel/preset-env": "^7.1.0",
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.2",
"classnames": "^2.2.6",
"css-loader": "^0.28.11",
"html-webpack-plugin": "^3.2.0",
"less": "^3.8.1",
"less-loader": "^4.1.0",
"mini-css-extract-plugin": "^0.4.0",
"my-local-ip": "^1.0.0",
"postcss-loader": "^2.1.5",
"style-loader": "^0.21.0",
"webpack": "^4.19.1",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.14"
}
}
21 changes: 21 additions & 0 deletions src/main/js/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Page from './components/page/Page';

const React = require('react');
const ReactDOM = require('react-dom');

/**
* Client Entry Point
*/
class App extends React.Component {

render() {
return <Page />
}
}

ReactDOM.render (
<App />,
document.getElementById('root')
)

export default App;
168 changes: 168 additions & 0 deletions src/main/js/components/game/Game.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import GameField from './GameField.jsx';
import ControlPanel from './controls/ControlPanel.jsx';

const React = require('react');
const KeyboardEventHandler = require('react-keyboard-event-handler');

/**
* Главное поле игры
*/
class Game extends React.Component {

roundId = null;
left = 'left';
right = 'right';
up = 'up';
down = 'down';
leftRight = [this.left, this.right];
upDown = [this.up, this.down];
speed = 1000;
boost = 0;

constructor(props) {
super(props);

this.state = {crash: true,
score: 0};
}

startRound = () => {

this.speed = this.props.speed;

this.setState({target: this.getNewTarget(),
snake: [{x: 39, y: 7}, {x: 38, y: 7}, {x: 37, y: 7}],
direction: this.right,
crash: false,
score: 0})

this.start();
}

start() {
this.roundId = setInterval(() => this.roundStep(), this.speed);
}

restartWithBoost() {
if (this.boost == 0) {
return;
}
clearInterval(this.roundId);
this.speed = this.speed - this.speed * this.boost / 100;
this.start();
}

stopRound() {
clearInterval(this.roundId);
}

roundStep() {
let {target, snake, direction, crash, score} = this.state;
const frontElement = this.getNextStep(snake[0], direction);

if (target.x === frontElement.x && target.y === frontElement.y) {
target = this.getNewTarget();
score = score + 1;
snake.unshift(frontElement);
this.restartWithBoost();
} else {
if (this.moveIsValid(snake, frontElement)) {
snake.pop();
snake.unshift(frontElement);
} else {
crash = true;
this.stopRound();
}
}

this.setState({target, snake, crash, score})
}

getNextStep(frontElement, direction) {
let x = frontElement.x;
let y = frontElement.y;
switch (direction) {
case this.left: x = x - 1; break;
case this.right: x = x + 1; break;
case this.up: y = y - 1; break;
case this.down: y = y + 1; break;
}

return {x, y};
}

moveIsValid(snake, frontElement) {
const {width, height} = this.props;
const x = frontElement.x;
const y = frontElement.y;
if (x < 0 || y < 0 || x >= width || y >= height) {
return false;
}

const lastIndex = snake.length - 1;
const hitch = snake.filter((item, index) => {
return index != lastIndex && item.x == x && item.y == y
});

if (hitch.length > 0) {
return false;
}

return true;
}

onBoostChange = (value) => {
this.boost = (!value || value < 1 || value > 50) ? 0 : value;
console.log("setBoost: " + this.boost)
}

render() {
const {target, snake, crash, score} = this.state;
const {width, height, step} = this.props;
const {left, right, up, down} = this;

return (
<div>
<GameField
width={width}
height={height}
step={step}
target={target}
snake={snake}
crash={crash}
/>
<ControlPanel
onClick={crash && this.startRound}
score={score}
onBoostChange={this.onBoostChange}
/>
<KeyboardEventHandler
handleKeys={[left, right, up, down]}
onKeyEvent={(key, e) => this.handleChangeDirection(key)}
/>
</div>
)
}

getNewTarget() {
return {x : this.getRandomInt(50), y : this.getRandomInt(30)};
}

handleChangeDirection(key) {
let {direction} = this.state;

// Forbid turn for 180 degrees
if (this.upDown.indexOf(key) > -1 && this.upDown.indexOf(direction) > -1
|| this.leftRight.indexOf(key) > -1 && this.leftRight.indexOf(direction) > -1) {
return;
}

this.setState({direction : key});
}

getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
}

export default Game;
14 changes: 14 additions & 0 deletions src/main/js/components/game/GameField.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
:local {
.gameField {
margin: auto;
background: #9fc5d4;
border: 5px solid #797979;
position: relative;
}

.crash {
background: #f1a5af;
border-color: #b76570;
transition: background .5s, border-color .5s;
}
}
41 changes: 41 additions & 0 deletions src/main/js/components/game/GameField.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Target from '../target/Target.jsx';
import SnakeElement from '../snake/SnakeElement.jsx';
import styles from './GameField.css';

const React = require('react');

/**
* Главное поле игры
*/
class GameField extends React.Component {

render() {

const {width, height, step, target, snake, crash} = this.props;

const style = {
width: width * step + "px",
height: height * step + "px",
};

const cm = styles.gameField + (crash ? " " + styles.crash : "");

return (
<div className={cm} style={style}>

{target && (
<Target x={target.x} y={target.y} size={step}/>
)}

{snake && snake.map((el, index) => {
return (
<SnakeElement x={el.x} y={el.y} size={step} key={index}/>
);
})}

</div>
)
}
}

export default GameField;
48 changes: 48 additions & 0 deletions src/main/js/components/game/controls/ControlPanel.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
:local {
.controlPanel {
padding: 16px;
background: #e2e2e2;
}
.controlPanel > * {
display: inline-block;
margin-right: 32px;
}

.startButton {
height: 32px;
width: 120px;
background: #eb992e;
border: none;
color: white;
letter-spacing: 4px;
}
.startButton:hover {
background: #d38c2e;
}
.startButton:active {
background: #b8761f;
}

.disabled {
background-color: #ccc !important;
color: grey !important;
}

.score {
font-size: 32px;
color: #eb992e;
vertical-align: middle;
float: right;
}
.boost {
color: #24c524;
font-size: 24px;
vertical-align: middle;
}
.boost input {
height: 32px;
width: 40px;
margin: 0 8px;
padding: 0 4px;
}
}
48 changes: 48 additions & 0 deletions src/main/js/components/game/controls/ControlPanel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import styles from './ControlPanel.css';

const React = require('react');

/**
* Панель управления
*/
class ControlPanel extends React.Component {

handleClick = () => {
const {onClick} = this.props;
onClick && onClick();
}

handleBoostChange= (value) => {
const {onBoostChange} = this.props;
onBoostChange && onBoostChange(value);
}

mayBeDisabled = (style, disabled) => {
return style + (disabled ? " " + styles.disabled : "");
}

render() {
const {onClick, score} = this.props;

return (
<div className={styles.controlPanel}>
<button disabled={!onClick}
className={this.mayBeDisabled(styles.startButton, !onClick)}
onClick={this.handleClick}>START
</button>
<div className={styles.boost}>
Boost:
<input
disabled={!onClick}
className={this.mayBeDisabled(styles.boost, !onClick)}
onChange={(e) => this.handleBoostChange(e.target.value)}
/>
%
</div>
<div className={styles.score}>Score: {score}</div>
</div>
);
}
}

export default ControlPanel;
Loading