Navigation TicTacToe Example
Reviewed By: hedgerwang Differential Revision: D2931575 fb-gh-sync-id: a60509315da4732d67b9b30bfc61f6d6a16be234 shipit-source-id: a60509315da4732d67b9b30bfc61f6d6a16be234
This commit is contained in:
parent
e9f2ff0ee1
commit
1beced56c8
|
@ -32,6 +32,7 @@ var EXAMPLES = {
|
|||
'Basic': require('./NavigationBasicExample'),
|
||||
'Animated Card Stack': require('./NavigationAnimatedExample'),
|
||||
'Composition': require('./NavigationCompositionExample'),
|
||||
'Tic Tac Toe': require('./NavigationTicTacToeExample'),
|
||||
};
|
||||
|
||||
var EXAMPLE_STORAGE_KEY = 'NavigationExampleExample';
|
||||
|
|
|
@ -0,0 +1,303 @@
|
|||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @providesModule NavigationTicTacToeExample
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
// $FlowFixMe : this does exist..
|
||||
NavigationExperimental,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View,
|
||||
} = React;
|
||||
const {
|
||||
Container: NavigationContainer,
|
||||
RootContainer: NavigationRootContainer,
|
||||
} = NavigationExperimental;
|
||||
|
||||
type GameGrid = Array<Array<?string>>;
|
||||
|
||||
const evenOddPlayerMap = ['o', 'x'];
|
||||
const rowLeterMap = ['a', 'b', 'c'];
|
||||
|
||||
function parseGame(game: string): GameGrid {
|
||||
const gameTurns = game ? game.split('-') : [];
|
||||
const grid = Array(3);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const row = Array(3);
|
||||
for (let j = 0; j < 3; j++) {
|
||||
const turnIndex = gameTurns.indexOf(rowLeterMap[i]+j);
|
||||
if (turnIndex === -1) {
|
||||
row[j] = null;
|
||||
} else {
|
||||
row[j] = evenOddPlayerMap[turnIndex % 2];
|
||||
}
|
||||
}
|
||||
grid[i] = row;
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
function playTurn(game: string, row: number, col: number): string {
|
||||
const turn = rowLeterMap[row] + col;
|
||||
return game ? (game + '-' + turn) : turn;
|
||||
}
|
||||
|
||||
function getWinner(gameString: string): ?string {
|
||||
const game = parseGame(gameString);
|
||||
for (var i = 0; i < 3; i++) {
|
||||
if (game[i][0] !== null && game[i][0] === game[i][1] &&
|
||||
game[i][0] === game[i][2]) {
|
||||
return game[i][0];
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < 3; i++) {
|
||||
if (game[0][i] !== null && game[0][i] === game[1][i] &&
|
||||
game[0][i] === game[2][i]) {
|
||||
return game[0][i];
|
||||
}
|
||||
}
|
||||
if (game[0][0] !== null && game[0][0] === game[1][1] &&
|
||||
game[0][0] === game[2][2]) {
|
||||
return game[0][0];
|
||||
}
|
||||
if (game[0][2] !== null && game[0][2] === game[1][1] &&
|
||||
game[0][2] === game[2][0]) {
|
||||
return game[0][2];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isGameOver(gameString: string): boolean {
|
||||
if (getWinner(gameString)) {
|
||||
return true;
|
||||
}
|
||||
const game = parseGame(gameString);
|
||||
for (var i = 0; i < 3; i++) {
|
||||
for (var j = 0; j < 3; j++) {
|
||||
if (game[i][j] === null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class Cell extends React.Component {
|
||||
cellStyle() {
|
||||
switch (this.props.value) {
|
||||
case 'x':
|
||||
return styles.cellX;
|
||||
case 'o':
|
||||
return styles.cellO;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
textStyle() {
|
||||
switch (this.props.value) {
|
||||
case 'x':
|
||||
return styles.cellTextX;
|
||||
case 'o':
|
||||
return styles.cellTextO;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<TouchableHighlight
|
||||
onPress={this.props.onPress}
|
||||
underlayColor="transparent"
|
||||
activeOpacity={0.5}>
|
||||
<View style={[styles.cell, this.cellStyle()]}>
|
||||
<Text style={[styles.cellText, this.textStyle()]}>
|
||||
{this.props.player}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function GameEndOverlay(props) {
|
||||
if (!isGameOver(props.game)) {
|
||||
return <View />;
|
||||
}
|
||||
const winner = getWinner(props.game);
|
||||
return (
|
||||
<View style={styles.overlay}>
|
||||
<Text style={styles.overlayMessage}>
|
||||
{winner ? winner + ' wins!' : 'It\'s a tie!'}
|
||||
</Text>
|
||||
<TouchableHighlight
|
||||
onPress={() => props.onNavigate(GameActions.Reset())}
|
||||
underlayColor="transparent"
|
||||
activeOpacity={0.5}>
|
||||
<View style={styles.newGame}>
|
||||
<Text style={styles.newGameText}>New Game</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
GameEndOverlay = NavigationContainer.create(GameEndOverlay);
|
||||
|
||||
function TicTacToeGame(props) {
|
||||
var rows = parseGame(props.game).map((cells, row) =>
|
||||
<View key={'row' + row} style={styles.row}>
|
||||
{cells.map((player, col) =>
|
||||
<Cell
|
||||
key={'cell' + col}
|
||||
player={player}
|
||||
onPress={() => props.onNavigate(GameActions.Turn(row, col))}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>EXTREME T3</Text>
|
||||
<View style={styles.board}>
|
||||
{rows}
|
||||
</View>
|
||||
<GameEndOverlay
|
||||
game={props.game}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
TicTacToeGame = NavigationContainer.create(TicTacToeGame);
|
||||
|
||||
const GameActions = {
|
||||
Turn: (row, col) => ({gameAction: 'turn', row, col }),
|
||||
Reset: (row, col) => ({gameAction: 'reset' }),
|
||||
};
|
||||
|
||||
function GameReducer(lastGame: ?string, action: Object): string {
|
||||
if (!lastGame || !action || !action.gameAction) {
|
||||
return lastGame || '';
|
||||
}
|
||||
if (action.gameAction === 'reset') {
|
||||
return '';
|
||||
}
|
||||
if (!isGameOver(lastGame) && action.gameAction === 'turn') {
|
||||
return playTurn(lastGame, action.row, action.col);
|
||||
}
|
||||
return lastGame;
|
||||
}
|
||||
|
||||
class NavigationTicTacToeExample extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<NavigationRootContainer
|
||||
reducer={GameReducer}
|
||||
persistenceKey="TicTacToeGame"
|
||||
renderNavigation={(game) => (
|
||||
<TicTacToeGame
|
||||
game={game}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
NavigationTicTacToeExample.GameView = TicTacToeGame;
|
||||
NavigationTicTacToeExample.GameReducer = GameReducer;
|
||||
NavigationTicTacToeExample.GameActions = GameActions;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'white'
|
||||
},
|
||||
title: {
|
||||
fontFamily: 'Chalkduster',
|
||||
fontSize: 39,
|
||||
marginBottom: 20,
|
||||
},
|
||||
board: {
|
||||
padding: 5,
|
||||
backgroundColor: '#47525d',
|
||||
borderRadius: 10,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
cell: {
|
||||
width: 80,
|
||||
height: 80,
|
||||
borderRadius: 5,
|
||||
backgroundColor: '#7b8994',
|
||||
margin: 5,
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
cellX: {
|
||||
backgroundColor: '#72d0eb',
|
||||
},
|
||||
cellO: {
|
||||
backgroundColor: '#7ebd26',
|
||||
},
|
||||
cellText: {
|
||||
borderRadius: 5,
|
||||
fontSize: 50,
|
||||
fontFamily: 'AvenirNext-Bold',
|
||||
},
|
||||
cellTextX: {
|
||||
color: '#19a9e5',
|
||||
},
|
||||
cellTextO: {
|
||||
color: '#b9dc2f',
|
||||
},
|
||||
overlay: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: 'rgba(221, 221, 221, 0.5)',
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
overlayMessage: {
|
||||
fontSize: 40,
|
||||
marginBottom: 20,
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
fontFamily: 'AvenirNext-DemiBold',
|
||||
textAlign: 'center',
|
||||
},
|
||||
newGame: {
|
||||
backgroundColor: '#887766',
|
||||
padding: 20,
|
||||
borderRadius: 5,
|
||||
},
|
||||
newGameText: {
|
||||
color: 'white',
|
||||
fontSize: 20,
|
||||
fontFamily: 'AvenirNext-DemiBold',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = NavigationTicTacToeExample;
|
Loading…
Reference in New Issue