-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3131c35
commit 1754f99
Showing
6 changed files
with
384 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import React from 'react' | ||
import { ModelStage, ModelBlock, ModelUserBlock } from './typeModels' | ||
import { Cell as StyledCell, Stage as StyledStage, Row as StyledRow } from './styles/stage' | ||
|
||
interface StageState { | ||
stage: ModelStage; | ||
userBlock: ModelUserBlock; | ||
predictionBlocks: ModelBlock[]; | ||
} | ||
interface StageProps { | ||
level: number; | ||
gameStatus: 'beforePlay' | 'play' | 'pause' | 'gameOver'; | ||
// save data | ||
dataPredictionBlocks: ModelBlock[]; | ||
dataStage: ModelStage; | ||
dataUserBlock: ModelUserBlock; | ||
keyCode: { | ||
left: number; | ||
right: number; | ||
down: number; | ||
rotate: number; | ||
pastDown: number; | ||
} | ||
} | ||
|
||
export default class Stage extends React.Component<StageProps, StageState> { | ||
private get timerSpeed () { | ||
// millisecond at one y line block drop | ||
// 20 level === 200, 1 level === 4000 | ||
return 4000 - 200 * (this.props.level - 1) | ||
} | ||
event: { timerId: number | boolean, key: boolean } = { | ||
key: false, | ||
timerId: false | ||
} | ||
|
||
shapeBlockPosPareser(block: ModelUserBlock): [number, number][] { | ||
const pos: [number, number][] = [] | ||
block.shape.forEach((row, rowIndex) => { | ||
row.forEach((ySettle, yIndex) => { | ||
if (ySettle) { | ||
pos.push([ | ||
block.standardPos[0] + rowIndex, | ||
block.standardPos[1] + yIndex | ||
]) | ||
} | ||
}) | ||
}) | ||
|
||
return pos | ||
} | ||
getConcatStageByBlock({ pos, background }: { pos: [number, number][]; background: string }, isSettle: boolean, stage?: ModelStage): ModelStage { | ||
const copyStage = stage ? [...stage] : [...this.state.stage] | ||
|
||
pos.forEach(posItem => { | ||
copyStage[posItem[0]][posItem[1]] = { | ||
background, | ||
settle: isSettle | ||
} | ||
}) | ||
|
||
return copyStage | ||
} | ||
|
||
eventHandler (key: 'addKey' | 'removeKey' | 'addTimer' | 'removeTimer') { | ||
switch (key) { | ||
case 'addKey': | ||
if (!this.event.key) { | ||
this.event.key = true | ||
} | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
keyboardEvent (e: KeyboardEvent) { | ||
switch (e.keyCode) { | ||
case this.props.keyCode.left: | ||
break; | ||
case this.props.keyCode.right: | ||
break; | ||
case this.props.keyCode.down: | ||
break; | ||
case this.props.keyCode.rotate: | ||
break; | ||
case this.props.keyCode.pastDown: | ||
break; | ||
} | ||
} | ||
|
||
constructor (props: StageProps) { | ||
super(props) | ||
|
||
this.state = { | ||
stage: props.dataStage, | ||
userBlock: props.dataUserBlock, | ||
predictionBlocks: props.dataPredictionBlocks | ||
} | ||
} | ||
|
||
shouldComponentUpdate (nextProps: StageProps) { | ||
if (this.props.gameStatus !== nextProps.gameStatus) return true | ||
else return false | ||
} | ||
|
||
render () { | ||
return ( | ||
<StyledStage cellCount={this.state.stage.length}> | ||
{ | ||
this.state.stage.map((rows, xIndex) => ( | ||
<StyledRow key={`tetris__x-${xIndex}`}> | ||
{ | ||
rows.map((cell, yIndex) => ( | ||
<StyledCell {...cell} key={`tetris__x-${xIndex}-${yIndex}`} /> | ||
)) | ||
} | ||
</StyledRow> | ||
)) | ||
} | ||
</StyledStage> | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,124 @@ | ||
import React from 'react' | ||
import { ModelScoreBoard, ModelStage, ModelBlock, ModelUserBlock } from './typeModels' | ||
import ScoreBoard from './scoreBoard' | ||
import Stage from './Stage' | ||
import getBlock from './logic/block' | ||
|
||
interface TetrisState { | ||
gameStatus: 'beforePlay' | 'play' | 'pause' | 'gameOver'; | ||
scoreBoard: ModelScoreBoard; | ||
sendToStage: { | ||
stage: ModelStage; | ||
predictionBlocks: ModelBlock[]; | ||
userBlock: ModelUserBlock; | ||
keyCode: { | ||
left: number, | ||
right: number, | ||
down: number, | ||
rotate: number, | ||
pastDown: number | ||
} | ||
} | ||
} | ||
interface TetrisData { | ||
stage?: ModelStage; | ||
scoreBoard?: ModelScoreBoard; | ||
predictionBlocks?: ModelBlock[]; | ||
userBlock?: ModelUserBlock; | ||
keyCode?: { | ||
left: number, | ||
right: number, | ||
down: number, | ||
rotate: number, | ||
pastDown: number | ||
} | ||
} | ||
export default class Tetris extends React.PureComponent<TetrisData, TetrisState> { | ||
|
||
gameBtHandler () { | ||
switch (this.state.gameStatus) { | ||
case 'play': | ||
this.setState({ | ||
gameStatus: 'pause' | ||
}) | ||
break; | ||
default: | ||
this.setState({ | ||
gameStatus: 'play' | ||
}) | ||
break; | ||
} | ||
} | ||
|
||
constructor (props: TetrisData) { | ||
super(props) | ||
|
||
const stageX = 10 | ||
const stageY = 20 | ||
|
||
const newUserBlock = getBlock() | ||
this.state = { | ||
sendToStage: { | ||
stage: | ||
props.stage || | ||
Array.from(new Array(stageX), () => new Array(stageY).fill({ backgrond: '', settle: false })), | ||
predictionBlocks: | ||
props.predictionBlocks || | ||
[getBlock(), getBlock(), getBlock()], | ||
userBlock: | ||
props.userBlock || | ||
{ | ||
...newUserBlock, | ||
standardPos: [Math.floor((stageX - newUserBlock.shape.length) / 2), 0] | ||
}, | ||
keyCode: | ||
props.keyCode || | ||
{ | ||
// left arrow | ||
left: 37, | ||
// right arrow | ||
right: 39, | ||
// down arrow | ||
down: 40, | ||
// s | ||
rotate: 89, | ||
// d | ||
pastDown: 68 | ||
} | ||
}, | ||
scoreBoard: | ||
props.scoreBoard || | ||
{ | ||
level: 1, | ||
score: 0, | ||
rows: 0 | ||
}, | ||
gameStatus: 'beforePlay', | ||
} | ||
|
||
this.gameBtHandler = this.gameBtHandler.bind(this) | ||
} | ||
|
||
export default class Tetris extends React.Component { | ||
render () { | ||
return ( | ||
<div> | ||
Tetris play! | ||
<Stage | ||
dataStage={this.state.sendToStage.stage} | ||
dataPredictionBlocks={this.state.sendToStage.predictionBlocks} | ||
dataUserBlock={this.state.sendToStage.userBlock} | ||
level={this.state.scoreBoard.level} | ||
gameStatus={this.state.gameStatus} | ||
keyCode={this.state.sendToStage.keyCode} | ||
/> | ||
<aside> | ||
<ScoreBoard {...this.state.scoreBoard} /> | ||
<button onClick={this.gameBtHandler}>{ | ||
this.state.gameStatus === 'play' ? | ||
'Pause' : | ||
'Play' | ||
}</button> | ||
</aside> | ||
</div> | ||
) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { ModelBlock } from '../typeModels' | ||
|
||
const blocks: ModelBlock[] = [ | ||
// ㄱ | ||
{ | ||
shape: [ | ||
[true, true, true], | ||
[true, false, false], | ||
[false, false, false] | ||
], | ||
background: '#186BD6' | ||
}, | ||
{ | ||
shape: [ | ||
[true, false, false], | ||
[true, true, true], | ||
[false, false, false] | ||
], | ||
background: '#DE651B' | ||
}, | ||
// ㄱ + ㄴ | ||
{ | ||
shape: [ | ||
[false, true, false], | ||
[true, true, false], | ||
[true, false, false] | ||
], | ||
background: '#ED5056' | ||
}, | ||
{ | ||
shape: [ | ||
[true, false, false], | ||
[true, true, false], | ||
[false, true, false] | ||
], | ||
background: '#4ABA54' | ||
}, | ||
// ㅡ | ||
{ | ||
shape: [ | ||
[true, true, true, true], | ||
[false, false, false, false], | ||
[false, false, false, false], | ||
[false, false, false, false] | ||
], | ||
background: '#68C5DB' | ||
}, | ||
// ㅗ | ||
{ | ||
shape: [ | ||
[true, false, false], | ||
[true, true, false], | ||
[true, false, false] | ||
], | ||
background: '#C57CEA' | ||
}, | ||
// ㅁ | ||
{ | ||
shape: [ | ||
[true, true], | ||
[true, true] | ||
], | ||
background: '#F6C643' | ||
} | ||
] | ||
|
||
const getBlock = (): ModelBlock => { | ||
return blocks[Math.floor(Math.random() * blocks.length)] | ||
} | ||
|
||
export default getBlock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react' | ||
import { ModelScoreBoard } from './typeModels' | ||
|
||
const ScoreBoard: React.SFC<ModelScoreBoard> = (props: ModelScoreBoard) => { | ||
return ( | ||
<div> | ||
<h3>score: {props.score}</h3> | ||
<h3>level: {props.level}</h3> | ||
<h3>rows: {props.rows}</h3> | ||
</div> | ||
) | ||
} | ||
|
||
export default React.memo(ScoreBoard) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import styled from 'styled-components' | ||
import { Cell as CellProps } from '../typeModels' | ||
|
||
const cellSize = { | ||
width: 30, | ||
height: 30 | ||
} | ||
|
||
export const Cell = styled.div<CellProps>` | ||
height: ${cellSize.height}px; | ||
box-sizing: border-box; | ||
border-bottom: 1px solid #000; | ||
background: ${props => | ||
props.background === '' ? | ||
'none' : props.background | ||
}; | ||
&:last-child { | ||
border-bottom: none | ||
}; | ||
` | ||
|
||
export const Row = styled.div` | ||
width: ${cellSize.width}px; | ||
border-right: 1px solid #000; | ||
box-sizing: border-box; | ||
float: left; | ||
&:last-child { | ||
border-right: none | ||
}; | ||
` | ||
export const Stage = styled.div<{ cellCount: number }>` | ||
width: ${props => props.cellCount * cellSize.width}px; | ||
border: 1px solid #000; | ||
&:after { | ||
content: ''; | ||
display: block; | ||
clear: both; | ||
}; | ||
` |
Oops, something went wrong.