Skip to content

Commit

Permalink
Add make move action to UI (#6)
Browse files Browse the repository at this point in the history
* Add make move action to UI

* Add make move call

* Show correct cards for make move and add instructions

* Fix tests
  • Loading branch information
neelsomani authored Apr 24, 2020
1 parent 74b9e56 commit 9cf3c98
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 29 deletions.
2 changes: 1 addition & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
api = LiteratureAPI(u_id=uuid.uuid4().hex,
logger=app.logger,
n_players=4,
time_limit=10)
time_limit=30)


@app.route('/')
Expand Down
23 changes: 21 additions & 2 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
}

.MoveDisplay {
width: 50%;
width: 80%;
top: 50%;
left: 50%;
position: absolute;
margin-left: -25%;
margin-left: -40%;
margin-top: -1em;
font-size: 2em;
text-align: center;
Expand All @@ -42,3 +42,22 @@
padding-top: 2%;
padding-bottom: 2%;
}

.MakeMoveCover {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
align-items: center;
justify-content: center;
display: flex;
}

.CardSelector {
text-align: center;
background-color: white;
border-radius: 10px;
padding: 1%;
}
55 changes: 52 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Players from './components/Players';
import MoveDisplay from './components/MoveDisplay';
import Timer from './components/Timer';
import VerticalCards from './components/VerticalCards';
import MakeMoveModal from './components/MakeMoveModal';
import './App.css';

class App extends Component {
Expand All @@ -12,9 +13,47 @@ class App extends Component {
uuid: '',
hand: [],
nPlayers: 0,
showMakeMoveModal: false
};
}

playCard(card) {
// Make sure the user is looking at the make move modal.
if (this.state.showMakeMoveModal && card) {
this.makeMove(card, this.state.toBeRespondent);
}
}

hideMakeMoveModal() {
this.setState({
showMakeMoveModal: false,
toBeRespondent: undefined
});
}

showMakeMoveModal(toBeRespondent) {
if (this.state.turn != this.state.playerN) return;
if (this.state.playerN % 2 == toBeRespondent % 2) return;
this.setState({
showMakeMoveModal: true,
toBeRespondent
});
}

makeMove(card, toBeRespondent) {
this.sendMessage({
'action': 'move',
'payload': {
'key': this.state.uuid,
'respondent': toBeRespondent,
'card': card
}
})
this.setState({
showMakeMoveModal: false
});
}

register(payload) {
const { uuid, player_n, n_players, time_limit } = payload;
this.setState({
Expand Down Expand Up @@ -47,6 +86,7 @@ class App extends Component {
respondent,
interrogator
})
if (turn != this.state.playerN) this.hideMakeMoveModal();
}

handleMessage(message) {
Expand All @@ -70,6 +110,7 @@ class App extends Component {
}

sendMessage(payload) {
console.log('Sending: ' + JSON.stringify(payload))
this.state.sender.send(JSON.stringify(payload));
}

Expand All @@ -89,7 +130,8 @@ class App extends Component {
);
this.setState({
'sender': sender
})
});
window.cards.playCard = (c) => { };
}

render() {
Expand All @@ -99,7 +141,8 @@ class App extends Component {
nPlayers={this.state.nPlayers}
playerN={this.state.playerN}
nCards={this.state.nCards}
turn={this.state.turn} />
turn={this.state.turn}
showModal={this.showMakeMoveModal.bind(this)} />
<MoveDisplay
success={this.state.success}
card={this.state.card}
Expand All @@ -108,8 +151,14 @@ class App extends Component {
<Timer
moveTimestamp={this.state.moveTimestamp}
timeLimit={this.state.timeLimit}
switchTeam={() => this.sendMessage({ 'action': 'switch_team' })} />
switchTeam={() => this.sendMessage({ 'action': 'switch_team' })}
turn={this.state.turn}
playerN={this.state.playerN} />
<VerticalCards handClass='Player-hand' cards={this.state.hand} />
{this.state.showMakeMoveModal && <MakeMoveModal
hand={this.state.hand}
hideModal={this.hideMakeMoveModal.bind(this)}
playCard={this.playCard.bind(this)} />}
</div>
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/App.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

class MockReconnectingWebSocket {
}

it('renders without crashing', () => {
const div = document.createElement('div');
window.ReconnectingWebSocket = MockReconnectingWebSocket;
window.cards = {}
ReactDOM.render(<App />, div);
});
6 changes: 5 additions & 1 deletion src/components/Card.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ export default class Card extends Component {
}

render() {
return <img class='card' src={this.cardMap[this.props.card]} />
return <img
class='card'
onClick={() => { this.props.playCard(this.props.card) }}
aria-card-name={this.props.card}
src={this.cardMap[this.props.card]} />
}
}
51 changes: 51 additions & 0 deletions src/components/MakeMoveModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { Component } from 'react';
import VerticalCards from './VerticalCards';

export default class MakeMoveModal extends Component {
sets = [
['A', '2', '3', '4', '5', '6'],
['8', '9', '10', 'J', 'Q', 'K']
]
suits = ['C', 'D', 'H', 'S']

constructor(props) {
super(props);
const hand = new Set(props.hand);
this.state = {
cards: []
}
this.sets.forEach((set) => {
this.suits.forEach((s) => {
let canAskHalf = false;
set.forEach((r) => {
if (hand.has(r + s)) canAskHalf = true;
});
if (!canAskHalf) return;
set.forEach((r) => {
if (!hand.has(r + s)) {
this.state.cards.push((r + s))
}
});
});
});
}

render() {
return <div class='MakeMoveCover'>
<div style={{
width: '100%',
height: '100%',
position: 'absolute',
top: 0,
left: 0,
zIndex: 5
}} onClick={this.props.hideModal}></div>
<div class='CardSelector' style={{ zIndex: 10 }}>
<VerticalCards
playCard={this.props.playCard}
suitClass='active-hand'
cards={this.state.cards} />
</div>
</div>
}
}
10 changes: 5 additions & 5 deletions src/components/MoveDisplay.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import React, { Component } from 'react';

export default class MoveDisplay extends Component {
render() {
if (!this.props.interrogator) {
return <div class='MoveDisplay'>
if (this.props.interrogator === undefined) {
return <div className='MoveDisplay'>
No move has been executed yet.
</div>
}
const success = this.props.success && 'Success' || 'Failure';
return <div class='MoveDisplay'>
{success}: Player {this.props.interrogator} requested
{this.props.card} from Player {this.props.respondent}
return <div className='MoveDisplay'>
{success}: Player {this.props.interrogator}
{' ' + this.props.card} from Player {this.props.respondent}
</div>
}
}
13 changes: 10 additions & 3 deletions src/components/Player.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import React, { Component } from 'react';
import PlayerIcon from './player.png';
import PlayerEvenIcon from './player-even.png';
import PlayerOddIcon from './player-odd.png';

export default class Players extends Component {
render() {
const icons = [PlayerEvenIcon, PlayerOddIcon];
let border = (this.props.turn == this.props.playerN) && {
border: 'solid 3px white'
} || {};
return <div class='Player' style={border}>
<img src={PlayerIcon} style={{ width: '100%' }} />
return <div
className='Player'
style={border}
onClick={() => this.props.showModal(this.props.playerN)}>
<img
src={icons[this.props.playerN % 2]}
style={{ width: '100%' }} />
<p style={{ textAlign: 'center', color: '#c0c0c0' }}>
Player {this.props.playerN} {
this.props.nCards && <p>{this.props.nCards} cards</p>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Players.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import Player from './Player';

export default class Players extends Component {
render() {
return <div class='Players'>
return <div className='Players'>
{[
...Array(this.props.nPlayers).keys()
].map((p) => {
if (p != this.props.playerN) {
return <Player
showModal={this.props.showModal}
turn={this.props.turn}
playerN={p}
nCards={(this.props.nCards || {})[p]} />
Expand Down
19 changes: 17 additions & 2 deletions src/components/Timer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React, { Component } from 'react';

import PlayerEvenIcon from './player-even.png';
import PlayerOddIcon from './player-odd.png';

export default class Timer extends Component {
constructor(props) {
super(props);
Expand Down Expand Up @@ -32,8 +35,20 @@ export default class Timer extends Component {

render() {
const timeRemaining = this.timeRemaining()
return <div class='Timer'>
{timeRemaining} second{(timeRemaining != 1) && 's'}
const img = [PlayerEvenIcon, PlayerOddIcon][this.props.playerN % 2]
const icon = (
<span style={{ paddingLeft: '10px', paddingRight: '10px' }}>
<img src={img} height={15} width={15} /> Player {this.props.playerN}
</span>
)
return <div className='Timer'>
{timeRemaining} second{(timeRemaining != 1) && 's'} //
{(this.props.playerN != -1)
&& this.props.playerN !== undefined
&& icon
|| ' Visitor'}
{(this.props.turn !== undefined && this.props.playerN == this.props.turn)
&& '// Click a player from the opposite team to request a card'}
</div>
}
}
51 changes: 40 additions & 11 deletions src/components/VerticalCards.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@ import React, { Component } from 'react';
import Card from './Card';

export default class VerticalCards extends Component {
sortCard(a, b) {
const mapping = {
'A': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
'10': 10,
'J': 11,
'Q': 12,
'K': 13
}
if (mapping[a] > mapping[b]) return 1;
else if (mapping[a] == mapping[b]) return 0;
return -1;
}

render() {
const suited = {
'C': [],
Expand All @@ -14,25 +35,33 @@ export default class VerticalCards extends Component {
suited[suit].push(c);
});
for (let s in suited) {
suited[s].sort();
suited[s].sort(this.sortCard);
}
let suitClass = "hand vhand-compact"
if (this.props.suitClass) {
suitClass += this.props.suitClass;
suitClass += ' ' + this.props.suitClass;
}
return (
<div class={this.props.handClass}>
<div class={suitClass}>
{suited['C'].map((c) => <Card card={c} />)}
<div className={this.props.handClass}>
<div className={suitClass}>
{suited['C'].map((c) => <Card
playCard={this.props.playCard}
card={c} />)}
</div>
<div class={suitClass}>
{suited['D'].map((c) => <Card card={c} />)}
<div className={suitClass}>
{suited['D'].map((c) => <Card
playCard={this.props.playCard}
card={c} />)}
</div>
<div class={suitClass}>
{suited['H'].map((c) => <Card card={c} />)}
<div className={suitClass}>
{suited['H'].map((c) => <Card
playCard={this.props.playCard}
card={c} />)}
</div>
<div class={suitClass}>
{suited['S'].map((c) => <Card card={c} />)}
<div className={suitClass}>
{suited['S'].map((c) => <Card
playCard={this.props.playCard}
card={c} />)}
</div>
</div>
)
Expand Down
File renamed without changes
Binary file added src/components/player-odd.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 9cf3c98

Please sign in to comment.