euchre-live

Euchre web-app for the socially distant family
git clone git://git.alexkarle.com/euchre-live.git
Log | Files | Refs | README | LICENSE

commit 54701cc9c08d684a25a1f5157a4c036ac5bc9fd6 (patch)
parent e7aa87e3756fbf4b44aa572dbd8d00fc6c8a50ac
Author: Chris Karle <chriskarle@hotmail.com>
Date:   Mon, 20 Apr 2020 01:46:42 -0400

TrumpPicker, CardTable

Creates TrumpPicker to give buttons for pass/pick and drop
menu for vote-2.

Completes basic play of tricks and new trump.

Need: score display, pause on final card (server update),
bug fixes.

Diffstat:
Massets/app.scss | 39+++++++++++++++++++++++++++++++++++++++
Massets/components/CardTable.js | 316+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Aassets/components/TrumpPicker.js | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 409 insertions(+), 46 deletions(-)

diff --git a/assets/app.scss b/assets/app.scss @@ -74,13 +74,19 @@ align-items: center; } .partner__info { + width: 100%; display: flex; justify-content: space-evenly; } .my__stack { float: right; +} +.my__hinfo { font-size: 1.5rem; } +.my__tinfo { + font-size: 1.3rem; +} .mh__card { width: 100px; } @@ -124,6 +130,32 @@ // .tb__center { // background-color: rgb(217, 249, 255); // } +.trick__card { + width: 80px; +} +.trick__holder, .trick__outer, .trick__grid, .trick__row { + height: 100%; +} +.left__trick, .right__trick { + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; +} +.mid__trick { + height: 100%; + display: flex; + flex-direction: column; +} +.me { + justify-content: flex-end; +} +.partner { + justify-content: flex-start; +} +.both { + justify-content: space-between; +} .trump__card { width: 80px; @@ -153,6 +185,13 @@ flex-direction: row; justify-content: flex-end; } +.tp1__stack { + display: flex; + flex-direction: column; + .bx--btn, .bx--dropdown__wrapper{ + margin-bottom: 3px; + } +} .table__header { height: 10vh; diff --git a/assets/components/CardTable.js b/assets/components/CardTable.js @@ -6,8 +6,15 @@ import {Logout32} from '@carbon/icons-react'; import SeatPicker from './SeatPicker'; import MainHand from './MainHand'; import HiddenHand from './HiddenHand'; +import TrumpPicker from './TrumpPicker'; const trumpPlacement = ['me', 'left', 'partner', 'right']; +const suit = { + H: 'Hearts', + D: 'Diamonds', + S: 'Spades', + C: 'Clubs' +} export default class CardTable extends React.Component { @@ -20,11 +27,25 @@ export default class CardTable extends React.Component { myHandInfo: 'mhi', myTurnInfo: 'mti', phase: 'lobby', - left: { name: '', seat: -1, handInfo: '', turnInfo: ' ' }, - partner: { name: '', seat: -1, handInfo: '', turnInfo: ' ' }, - right: { name: '', seat: -1, handInfo: '', turnInfo: '' }, + leftName: '', + leftSeat: -1, + leftHandInfo: '', + leftTurnInfo: '', + partnerName: '', + partnerSeat: -1, + partnerHandInfo: '', + partnerTurnInfo: '', + rightName: '', + rightSeat: -1, + rightHandInfo: '', + rightTurnInfo: '', + trump: '', trumpPlace: '', - trumpNom: '' + trumpNom: '', + turnSeat: -1, + dealSeat: -1, + table: [], + handLengths: [] }; }; @@ -47,6 +68,12 @@ export default class CardTable extends React.Component { case 'vote': this.processVote(msg); break; + case 'dealer_swap': + this.processSwap(msg); + break; + case 'play': + this.processPlay(msg); + break; default: break; } @@ -69,13 +96,116 @@ export default class CardTable extends React.Component { processVote = (msg) => { if (this.state.phase == 'lobby') { this.gameStartSetup(msg); + } else if (this.state.phase == 'play') { + this.trumpStartSetup(msg); } + const {leftSeat, rightSeat, partnerSeat, mySeat} = this.state; + const phaseString = msg.game.pass_count > 3 ? 'vote2' : 'vote'; + let turnInfo = ['', '', '', '']; + turnInfo[msg.game.turn] = 'trump?'; this.setState({ - phase: 'vote', - myCards: msg.hand + phase: phaseString, + myCards: msg.hand, + turnSeat: msg.game.turn, + leftTurnInfo: turnInfo[leftSeat], + rightTurnInfo: turnInfo[rightSeat], + partnerTurnInfo: turnInfo[partnerSeat], + myTurnInfo: turnInfo[mySeat] }); }; + processSwap = msg => { + const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat} = this.state; + let turnInfo = ['', '', '', '']; + turnInfo[dealSeat] = 'Swapping...'; + this.setState({ + phase: 'swap', + leftTurnInfo: turnInfo[leftSeat], + rightTurnInfo: turnInfo[rightSeat], + partnerTurnInfo: turnInfo[partnerSeat], + myTurnInfo: turnInfo[mySeat] + }); + } + + trickMsg = (num, action) => { + let retVal; + if (num == 0) { + retVal = action ? 'action' : ''; + } else { + retVal = num == 1 ? '1 trick' : (num + ' tricks'); + if (action) { + retVal = retVal + ', action'; + } + } + return retVal; + } + + processPlay = msg => { + const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat, phase} = this.state; + let turnInfo = []; + if (phase == 'vote' || phase == 'vote2' || phase == 'swap') { + this.handStartSetup(msg); + } + for (let i = 0; i < 4; i++) { + const tricks = msg.game.tricks[i]; + let tInf = this.trickMsg(tricks, (msg.game.turn == i)); + turnInfo.push(tInf); + } + this.setState({ + phase: 'play', + myCards: msg.hand, + table: msg.game.table, + handLengths: msg.game.hand_lengths, + leftTurnInfo: turnInfo[leftSeat], + rightTurnInfo: turnInfo[rightSeat], + partnerTurnInfo: turnInfo[partnerSeat], + myTurnInfo: turnInfo[mySeat] + }); + + } + + handStartSetup = msg => { + const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat} = this.state; + let handInfo = ['', '', '', '']; + const caller = msg.game.out_player > -1 ? 'Alone' : 'Caller'; + const callSeat = msg.game.caller; + if (callSeat == dealSeat) { + handInfo[dealSeat] = 'Dealer, ' + caller; + } else { + handInfo[dealSeat] = 'Dealer'; + handInfo[callSeat] = caller; + } + this.setState({ + trump: msg.game.trump, + leftHandInfo: handInfo[leftSeat], + rightHandInfo: handInfo[rightSeat], + partnerHandInfo: handInfo[partnerSeat], + myHandInfo: handInfo[mySeat] + }); + + } + + trumpStartSetup = (msg) => { + const {leftSeat, rightSeat, partnerSeat, mySeat, dealSeat} = this.state; + let handInfo = ['', '', '', '']; + const newDeal = msg.game.dealer; + handInfo[newDeal] = 'Dealer'; + let tpIndex = msg.game.dealer - mySeat; + tpIndex = (tpIndex < 0) ? tpIndex + 4 : tpIndex; + const trumpPlace = trumpPlacement[tpIndex]; + this.setState({ + trump: '', + trumpPlace: trumpPlace, + dealSeat: newDeal, + trumpNom: msg.game.trump_nominee, + leftHandInfo: handInfo[leftSeat], + rightHandInfo: handInfo[rightSeat], + partnerHandInfo: handInfo[partnerSeat], + myHandInfo: handInfo[mySeat] + }); + + } + gameStartSetup = (msg) => { const { playerNames, mySeat } = this.state; let handInfo = [' ', ' ', ' ', ' ']; @@ -90,28 +220,23 @@ export default class CardTable extends React.Component { const trumpPlace = trumpPlacement[tpIndex]; console.log('trumpPlace:', trumpPlace); this.setState ({ - left : { - name: playerNames[leftSeat], - seat: leftSeat, - handInfo: handInfo[leftSeat], - turnInfo: turnInfo[leftSeat] - }, - partner : { - name: playerNames[partnerSeat], - seat: partnerSeat, - handInfo: handInfo[partnerSeat], - turnInfo: turnInfo[partnerSeat] - }, - right : { - name: playerNames[rightSeat], - seat: rightSeat, - handInfo: handInfo[rightSeat], - turnInfo: turnInfo[rightSeat] - }, + leftName: playerNames[leftSeat], + leftSeat: leftSeat, + leftHandInfo: handInfo[leftSeat], + leftTurnInfo: turnInfo[leftSeat], + partnerName: playerNames[partnerSeat], + partnerSeat: partnerSeat, + partnerHandInfo: handInfo[partnerSeat], + partnerTurnInfo: turnInfo[partnerSeat], + rightName: playerNames[rightSeat], + rightSeat: rightSeat, + rightHandInfo: handInfo[rightSeat], + rightTurnInfo: turnInfo[rightSeat], myHandInfo : handInfo[mySeat], myTurnInfo : turnInfo[mySeat], trumpPlace : trumpPlace, - trumpNom: msg.game.trump_nominee + trumpNom: msg.game.trump_nominee, + dealSeat: msg.game.dealer }); }; @@ -133,23 +258,108 @@ export default class CardTable extends React.Component { sendStart = (startDealer) => { this.props.client.send(JSON.stringify({ action: 'start_game', start_seat: startDealer - })) + })); console.log('start game, dealer = ', startDealer); }; + sendVote = (voteObject) => { + const voteString = JSON.stringify(voteObject); + console.log('sendVote:', voteString); + this.props.client.send(voteString); + }; + sendCard = (index) => { - console.log('card click ', index); + const {phase, myCards} = this.state; + console.log('card click ', myCards[index]); + if (phase == 'swap') { + this.props.client.send(JSON.stringify({ + action:'dealer_swap', card: myCards[index] + })); + } else if (phase == 'play') { + this.props.client.send(JSON.stringify({ + action:'play_card', card: myCards[index] + })); + } + }; + + genTrick = () => { + let retVal = []; + const { table, mySeat, leftSeat, rightSeat, partnerSeat } = this.state; + const myCard = table[mySeat]; + const leftCard = table[leftSeat]; + const partnerCard = table[partnerSeat]; + const rightCard = table[rightSeat]; + const mySrc = 'cards/' + myCard + '.svg'; + const leftSrc = 'cards/' + leftCard + '.svg'; + const partnerSrc = 'cards/' + partnerCard + '.svg'; + const rightSrc = 'cards/' + rightCard + '.svg'; + let midClass = 'mid__trick'; + if (myCard && partnerCard) { + midClass += ' both'; + } else if (myCard) { + midClass += ' me'; + } else if (partnerCard) { + midClass += ' partner'; + } + retVal.push ( + <div className="trick__outer"> + <Grid className="trick__grid"> + <Row className="trick__row"> + <Column> + <div className="left__trick"> + {leftCard && ( + <div className="trick__div"> + <img className="trick__card" src={leftSrc} /> + </div> + )} + </div> + </Column> + <Column> + <div className={midClass}> + {partnerCard && ( + <div className="trick__div"> + <img className="trick__card" src={partnerSrc} /> + </div> + )} + {myCard && ( + <div className="trick__div"> + <img className="trick__card" src={mySrc} /> + </div> + )} + </div> + </Column> + <Column> + <div className="right__trick"> + {rightCard && ( + <div className="trick__div"> + <img className="trick__card" src={rightSrc} /> + </div> + )} + </div> + </Column> + </Row> + </Grid> + </div> + ); + return retVal; } render () { - const { playerNames, mySeat, phase, left, partner, right, myCards, - myHandInfo, myTurnInfo, trumpPlace, trumpNom } = this.state; + const { playerNames, mySeat, phase, myCards, myHandInfo, myTurnInfo, + partnerName, partnerHandInfo, partnerTurnInfo, partnerSeat, leftName, leftTurnInfo, leftHandInfo, leftSeat, + rightName, rightHandInfo, rightTurnInfo, rightSeat, trumpPlace, trumpNom, turnSeat, + dealSeat, trump, handLengths } = this.state; const {name, tableName} = this.props; const showSeatPicker = phase == 'lobby'; - const showTrump = phase == 'vote'; + const showTrump = (phase == 'vote') || (phase == 'vote2') || (phase == 'swap'); + const showTrumpPicker = showTrump && (turnSeat == mySeat); + const showSwap = (phase == 'swap') && (dealSeat == mySeat); + const showInfo = !showSeatPicker && !showTrumpPicker && !showSwap; const welcomeMsg = 'Welcome to the ' + tableName + ' table, ' + name + '!'; const tcp = "trump__holder " + trumpPlace; - const trumpImage = 'cards/' + trumpNom + '.svg'; + const trumpImage = phase == 'vote' ? 'cards/' + trumpNom + '.svg' : 'cards/1B.svg'; + const trumpMsg = phase == 'play' ? suit[trump] + ' are trump' : ''; + const trickDisplay = phase == 'play' ? this.genTrick() : []; return ( <div id="table"> <Grid> @@ -175,13 +385,13 @@ export default class CardTable extends React.Component { <Column className="tt__center" sm={2}> {!showSeatPicker && ( <div className="partner__stack"> - <div className="player__name">{partner.name}</div> + <div className="player__name">{partnerName}</div> <div className="partner__info"> - <div className="play__hinfo">{partner.handInfo}</div> - <div className="play__tinfo">{partner.turnInfo}</div> + <div className="play__hinfo">{partnerHandInfo}</div> + <div className="play__tinfo">{partnerTurnInfo}</div> </div> <HiddenHand - numCards={5} /> + numCards={handLengths[partnerSeat]} /> </div>)} </Column> <Column className="tt__right" sm={1}> @@ -191,11 +401,11 @@ export default class CardTable extends React.Component { <Column className="tm__left" sm={1}> {!showSeatPicker && ( <div className="vert__stack"> - <div className="player__name">{left.name}</div> - <div className="play__hinfo">{left.handInfo}</div> - <div className="play__tinfo">{left.turnInfo}</div> + <div className="player__name">{leftName}</div> + <div className="play__hinfo">{leftHandInfo}</div> + <div className="play__tinfo">{leftTurnInfo}</div> <HiddenHand - numCards={5} /> + numCards={handLengths[leftSeat]} /> </div>)} </Column> <Column className="tm__center" sm={2}> @@ -214,24 +424,40 @@ export default class CardTable extends React.Component { </div> </div> )} + { (phase=='play') && ( + <div className="trick__holder">{trickDisplay}</div> + )} </Column> <Column className="tm__right" sm={1}> {!showSeatPicker && ( <div className="vert__stack"> - <div className="player__name">{right.name}</div> - <div className="play__hinfo">{right.handInfo}</div> - <div className="play__tinfo">{right.turnInfo}</div> + <div className="player__name">{rightName}</div> + <div className="play__hinfo">{rightHandInfo}</div> + <div className="play__tinfo">{rightTurnInfo}</div> <HiddenHand - numCards={5} /> + numCards={handLengths[rightSeat]} /> </div>)} </Column> </Row> <Row className="table__bot"> <Column className="tb__left" sm={1}> - {!showSeatPicker && ( + {showInfo && ( <div className="my__stack"> <div className="my__hinfo">You: {myHandInfo}</div> <div className="my__tinfo">{myTurnInfo}</div> + <div className="my__tinfo">{trumpMsg}</div> + </div> + )} + {showTrumpPicker && ( + <TrumpPicker + trumpCard={trumpNom} + phaseTwo={phase == 'vote2'} + myDeal={dealSeat == mySeat} + handleVote={this.sendVote} /> + )} + {showSwap && ( + <div className="my__stack"> + <div className="my_tinfo">Click a card to discard:</div> </div> )} </Column> @@ -243,8 +469,6 @@ export default class CardTable extends React.Component { /> )} </Column> - {/* <Column className="tb__right" sm={1}> - </Column> */} </Row> </Grid> diff --git a/assets/components/TrumpPicker.js b/assets/components/TrumpPicker.js @@ -0,0 +1,100 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {Button, Dropdown} from 'carbon-components-react'; + +const remain = { //HDSC order by Game.pm + H: ['Diamonds', 'Spades', 'Clubs'], + D: ['Hearts', 'Spades', 'Clubs'], + S: ['Hearts', 'Diamonds', 'Clubs'], + C: ['Hearts', 'Diamonds', 'Spades'] +} +//NB usage w subs: voteOptions.order(<suit>) +const voteOptions = { + pass: {action: 'order', vote: 'pass'}, + order: suit => ({action: 'order', vote: suit, loner: 0}), + alone: suit => ({action: 'order', vote: suit, loner: 1}) +} + +class TrumpPicker extends React.Component { + + constructor(props) { + super(props); + this.state = { + suitPick: 'U' + } + }; + + componentDidUpdate (prevProps) { + const { trumpCard, phaseTwo } = this.props; + if ((trumpCard != prevProps.trumpCard) || (phaseTwo != prevProps.phaseTwo)){ + this.setState ({ + suitPick: 'U' + }) + } + } + + suitChange = event => { + const pick = event.selectedItem; + this.setState({ + suitPick: pick.substring(0,1) + }); + }; + + + render () { + const { trumpCard, phaseTwo, myDeal, handleVote } = this.props; + const { suitPick } = this.state; + const trumpSuit = trumpCard ? trumpCard.substring(1) : 'U'; + const orderLabel = myDeal ? 'Pick up' : 'Order'; + const aloneLabel = myDeal ? 'Pick, Alone' : 'Order, Alone'; + const pickerLabels = remain[trumpSuit]; + const voteSuit = phaseTwo ? suitPick : trumpSuit; + const orderVote = voteOptions.order(voteSuit); + const aloneVote = voteOptions.alone(voteSuit); + const disableOrder = phaseTwo && suitPick == 'U'; + return ( + <div id="TrumpPicker" className="trump__picker"> + <div className="tp1__stack"> + <Button + className="p1p__button" + kind="primary" + size="small" + onClick={()=>{handleVote(voteOptions.pass)}} + >Pass</Button> + {phaseTwo && ( + <Dropdown + id="trump__drop" + className="trump__drop" + label="Choose Suit..." + size="sm" + itemToElement={null} + items={pickerLabels} + onChange={(event)=>this.suitChange(event)} + /> + )} + <Button + className="p1o__button" + kind="primary" + size="small" + disabled={disableOrder} + onClick={()=>{handleVote(orderVote)}} + >{orderLabel}</Button> + <Button + className="p1a__button" + kind="primary" + size="small" + disabled={disableOrder} + onClick={()=>{handleVote(aloneVote)}} + >{aloneLabel}</Button> + </div> + </div> + ); + } +} +TrumpPicker.propTypes = { + trumpCard: PropTypes.string, + phaseTwo: PropTypes.bool, + myDeal: PropTypes.bool, + handleVote: PropTypes.func, +} +export default TrumpPicker;