Adding AI.

This commit is contained in:
2020-04-26 19:49:59 -07:00
parent 250fdb9ca7
commit db47e5e6e9
3 changed files with 392 additions and 269 deletions

View File

@@ -34,7 +34,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faUser, faUsers, faTractor, faWindowRestore,
faDollarSign, faTimes, faExchangeAlt,
faInfoCircle, faArrowUp, faArrowDown, faAward,
faBan, faArrowCircleLeft } from '@fortawesome/free-solid-svg-icons'
faBan, faArrowCircleLeft, faPlusCircle } from '@fortawesome/free-solid-svg-icons'
import { GroupBox, Row, Col, Button } from '../widgets.jsx'
import SpaceNode from './SpaceNode.jsx'
@@ -48,21 +48,22 @@ import { setSelectedCard, setMessagePanelSpace, setMPDims, movePlayer,
import { buy, roll, endTurn, loan, trade, submitTradeAccept,
submitTradeDeny, submitTradeCancel, audit,
buyUncleBert, skip, endAiTurn, startGame, readyToStart,
leaveGame, kickPlayer, toggleRevealForTrade } from './interface.js'
leaveGame, kickPlayer, toggleRevealForTrade,
addAIPlayer } from './interface.js'
function netWorth(player) {
return ((player.assets.hay + player.assets.grain) * 2000) +
(player.assets.fruit * 5000) +
(player.assets.cows * 500) +
((player.assets.harvester + player.assets.tractor) * 10000) +
(player.assets.fruit * 5000) +
(player.assets.cows * 500) +
((player.assets.harvester + player.assets.tractor) * 10000) +
player.displayCash - player.debt;
}
function assetsValue(player) {
return ((player.assets.hay + player.assets.grain) * 2000) +
(player.assets.fruit * 5000) +
(player.assets.cows * 500) +
((player.assets.harvester + player.assets.tractor) * 10000);
(player.assets.fruit * 5000) +
(player.assets.cows * 500) +
((player.assets.harvester + player.assets.tractor) * 10000);
}
class ResourceUnit extends React.Component {
@@ -617,227 +618,227 @@ class TradeContainer extends React.Component {
return (
<GroupBox title='Trade'>
{this.state.showCards ? (
<TradeCards player={this.state.showCardsPlayer}
me={player}
tradeCard={this.tradeCard}
goBack={this.viewPlayerCards}
cardsBeingTraded={this.state.sendCards.map(c => c.id).concat(this.state.receiveCards.map(c => c.id))}
/>
<TradeCards player={this.state.showCardsPlayer}
me={player}
tradeCard={this.tradeCard}
goBack={this.viewPlayerCards}
cardsBeingTraded={this.state.sendCards.map(c => c.id).concat(this.state.receiveCards.map(c => c.id))}
/>
) : (
<>
<Row>
<Col width="12">
<div className="trade-player-container">
<h4>
{player.name}
{player.cards.length > 0 ? (
<>
{' - '}
<a onClick={e => this.viewPlayerCards(e, player)}>
<FontAwesomeIcon icon={faWindowRestore} /> Trade Cards
</a>
</>
) : (<></>)}
</h4>
{Object.keys(player.ridges).map((ridge, idx) => (
<Fragment key={'player-trade' + ridge}>
{!tradeProposed && (player.ridges[ridge] > 0) && !toTrade[ridge] ? (
<Button className="tiny"
onClick={() => this.tradeAsset(ridge, player.ridges[ridge], true)}>
<FontAwesomeIcon icon={faArrowDown} /> {ridgeNames[idx]} {'('}{player.ridges[ridge]} cows{')'}
</Button>
) : (<></>)}
{' '}
</Fragment>
))}
<div className='resource-unit-container'>
{this.resources.map((o, i) => {
let amount = o.key === 'money' ? Math.floor(player.cash / 1000) : player.assets[o.key];
if (o.key === 'cows') { amount = player.assets.cows - playerRidgeCows(player); }
return (
<ResourceUnit img={o.img ? o.img : false} h={o.h} s={o.s} label={o.label}
key={i}
className={o.key === 'money' ? 'double-width' : false}
amount={amount + Math.min(0, toTrade[o.key])}
>
{o.icon ? (
<>
<FontAwesomeIcon icon={o.icon} />(K)
<br />
</>
) : ''}
{amount + Math.min(0, toTrade[o.key])}
{o.icon ? (<br />) : ''}
{!tradeProposed && ((amount + Math.min(0, toTrade[o.key])) > 0 || toTrade[o.key] > 0) ? (
<>
<Button className="tiny"
onClick={() => this.tradeAsset(o.key, o.amount * -1)}>
<FontAwesomeIcon icon={faArrowDown} />
</Button>
{o.key === 'money' && (amount + Math.min(0, toTrade[o.key])) >= 10 ? (
<Button className="tiny"
onClick={() => this.tradeAsset(o.key, o.amount * -10)}>
<FontAwesomeIcon icon={faArrowDown} />
<FontAwesomeIcon icon={faArrowDown} />
</Button>
) : ''}
</>
) : (<></>)}
</ResourceUnit>
);
})}
</div>
</div>
</Col>
</Row>
<Row>
<Col width="12">
{Object.keys(otherPlayer ? otherPlayer.ridges : []).map((ridge, idx) => (
<Fragment key={'to-trade' + ridge}>
{(otherPlayer.ridges[ridge] > 0) && toTrade[ridge] ? (
<Button className="tiny"
onClick={() => this.tradeAsset(ridge, false)}>
<FontAwesomeIcon icon={faArrowDown} /> {ridgeNames[idx]} {'('}{otherPlayer.ridges[ridge]} cows{')'}
</Button>
) : (<></>)}
{' '}
</Fragment>
))}
{this.state.receiveCards.map((card, idx) => (
<Button className="tiny"
key={idx}
onClick={() => this.unTradeCard(card.id)}>
{card.summary}
</Button>
))}
</Col>
</Row>
<Row>
<Col width="12">
<div className='resource-unit-container resources-to-trade'>
{this.resources.map((o, i) => (
<ResourceUnit h={o.h} s={o.s} label={o.label}
key={i}
className={(this.tradeClass(toTrade[o.key])) + (o.key === 'money' ? ' double-width' : '')}
amount={toTrade[o.key]}
>
{Math.abs(toTrade[o.key])}
</ResourceUnit>
))}
</div>
</Col>
</Row>
<Row>
<Col width="12">
{Object.keys(player.ridges).map((ridge, idx) => (
<Fragment key={'to-trade' + ridge}>
{(player.ridges[ridge] > 0) && toTrade[ridge] ? (
<Button className="tiny"
onClick={() => this.tradeAsset(ridge, false)}>
<FontAwesomeIcon icon={faArrowUp} /> {ridgeNames[idx]} {'('}{player.ridges[ridge]} cows{')'}
</Button>
) : (<></>)}
{' '}
</Fragment>
))}
{this.state.sendCards.map((card, idx) => (
<Button className="tiny"
key={idx}
onClick={() => this.unTradeCard(card.id)}>
{card.summary}
</Button>
))}
</Col>
</Row>
<Row>
<Col width="12">
<div className="trade-player-container">
{otherPlayer ? (
<div className='resource-unit-container'>
{this.resources.map((o, i) => {
let amount = o.key === 'money' ? 99 : otherPlayer.assets[o.key];
if (o.key === 'cows') { amount = otherPlayer.assets.cows - playerRidgeCows(otherPlayer); }
return (
<ResourceUnit img={o.img ? o.img : false} h={o.h} s={o.s} label={o.label}
key={i}
className={o.key === 'money' ? 'double-width' : false}
amount={amount - Math.max(0, toTrade[o.key])}
>
{o.icon ? (
<>
<FontAwesomeIcon icon={o.icon} />(K)
<br />
</>
) : ''}
{amount - Math.max(0, toTrade[o.key])}
{o.icon ? (<br />) : ''}
{!tradeProposed && ((amount - Math.max(0, toTrade[o.key])) > 0 || toTrade[o.key] < 0) ? (
<>
<Button className="tiny"
onClick={() => this.tradeAsset(o.key, o.amount)}>
<FontAwesomeIcon icon={faArrowUp} />
</Button>
{o.key === 'money' && (amount - Math.min(0, toTrade[o.key])) >= 10 ? (
<Button className="tiny"
onClick={() => this.tradeAsset(o.key, o.amount * 10)}>
<FontAwesomeIcon icon={faArrowUp} />
<FontAwesomeIcon icon={faArrowUp} />
</Button>
) : ''}
</>
) : (<></>)}
</ResourceUnit>
);
})}
<>
<Row>
<Col width="12">
<div className="trade-player-container">
<h4>
{player.name}
{player.cards.length > 0 ? (
<>
{' - '}
<a onClick={e => this.viewPlayerCards(e, player)}>
<FontAwesomeIcon icon={faWindowRestore} /> Trade Cards
</a>
</>
) : (<></>)}
</h4>
{Object.keys(player.ridges).map((ridge, idx) => (
<Fragment key={'player-trade' + ridge}>
{!tradeProposed && (player.ridges[ridge] > 0) && !toTrade[ridge] ? (
<Button className="tiny"
onClick={() => this.tradeAsset(ridge, player.ridges[ridge], true)}>
<FontAwesomeIcon icon={faArrowDown} /> {ridgeNames[idx]} {'('}{player.ridges[ridge]} cows{')'}
</Button>
) : (<></>)}
{' '}
</Fragment>
))}
<div className='resource-unit-container'>
{this.resources.map((o, i) => {
let amount = o.key === 'money' ? Math.floor(player.cash / 1000) : player.assets[o.key];
if (o.key === 'cows') { amount = player.assets.cows - playerRidgeCows(player); }
return (
<ResourceUnit img={o.img ? o.img : false} h={o.h} s={o.s} label={o.label}
key={i}
className={o.key === 'money' ? 'double-width' : false}
amount={amount + Math.min(0, toTrade[o.key])}
>
{o.icon ? (
<>
<FontAwesomeIcon icon={o.icon} />(K)
<br />
</>
) : ''}
{amount + Math.min(0, toTrade[o.key])}
{o.icon ? (<br />) : ''}
{!tradeProposed && ((amount + Math.min(0, toTrade[o.key])) > 0 || toTrade[o.key] > 0) ? (
<>
<Button className="tiny"
onClick={() => this.tradeAsset(o.key, o.amount * -1)}>
<FontAwesomeIcon icon={faArrowDown} />
</Button>
{o.key === 'money' && (amount + Math.min(0, toTrade[o.key])) >= 10 ? (
<Button className="tiny"
onClick={() => this.tradeAsset(o.key, o.amount * -10)}>
<FontAwesomeIcon icon={faArrowDown} />
<FontAwesomeIcon icon={faArrowDown} />
</Button>
) : ''}
</>
) : (<></>)}
</ResourceUnit>
);
})}
</div>
</div>
) : (<></>)}
{Object.keys(otherPlayer ? otherPlayer.ridges : []).map((ridge, idx) => (
<Fragment key={'other-player-trade' + ridge}>
{!tradeProposed && ((otherPlayer.ridges[ridge] > 0) && !toTrade[ridge]) ? (
<Button className="tiny"
onClick={() => this.tradeAsset(ridge, otherPlayer.ridges[ridge], true)}>
<FontAwesomeIcon icon={faArrowUp} /> {ridgeNames[idx]} {'('}{otherPlayer.ridges[ridge]} cows{')'}
</Button>
) : (<></>)}
{' '}
</Fragment>
))}
<select onChange={this.selectPlayer}
value={otherPlayer.name}>
{this.props.game.otherPlayers.map(p => (
<option key={p.player.name} value={p.player.name}>
{p.player.name}
</option>
))}
</select>
{otherPlayer && otherPlayer.revealedCards.length > 0 ? (
<a onClick={e => this.viewPlayerCards(e, otherPlayer)}>
<FontAwesomeIcon icon={faWindowRestore} /> Trade Cards
</a>
) : (<></>)}
</div>
</Col>
</Row>
<Row>
<Col width="12">
{player.trade.error ? (
<p><b>ERROR:</b> {player.trade.error}</p>
) : (<></>)}
{tradeProposed && player.trade.originator === player.name ? (
<Button onClick={submitTradeCancel}>Cancel trade</Button>
) : (<></>)}
{tradeProposed && player.trade.originator !== player.name ? (
<>
<Button onClick={submitTradeAccept}>Accept trade</Button>{' '}
<Button onClick={submitTradeCancel}>Deny trade</Button>
</>
) : (<></>)}
{!tradeProposed ? (
<Button onClick={this.propose}>Propose Trade</Button>
) : (<></>)}
</Col>
</Row>
</>)}
</Col>
</Row>
<Row>
<Col width="12">
{Object.keys(otherPlayer ? otherPlayer.ridges : []).map((ridge, idx) => (
<Fragment key={'to-trade' + ridge}>
{(otherPlayer.ridges[ridge] > 0) && toTrade[ridge] ? (
<Button className="tiny"
onClick={() => this.tradeAsset(ridge, false)}>
<FontAwesomeIcon icon={faArrowDown} /> {ridgeNames[idx]} {'('}{otherPlayer.ridges[ridge]} cows{')'}
</Button>
) : (<></>)}
{' '}
</Fragment>
))}
{this.state.receiveCards.map((card, idx) => (
<Button className="tiny"
key={idx}
onClick={() => this.unTradeCard(card.id)}>
{card.summary}
</Button>
))}
</Col>
</Row>
<Row>
<Col width="12">
<div className='resource-unit-container resources-to-trade'>
{this.resources.map((o, i) => (
<ResourceUnit h={o.h} s={o.s} label={o.label}
key={i}
className={(this.tradeClass(toTrade[o.key])) + (o.key === 'money' ? ' double-width' : '')}
amount={toTrade[o.key]}
>
{Math.abs(toTrade[o.key])}
</ResourceUnit>
))}
</div>
</Col>
</Row>
<Row>
<Col width="12">
{Object.keys(player.ridges).map((ridge, idx) => (
<Fragment key={'to-trade' + ridge}>
{(player.ridges[ridge] > 0) && toTrade[ridge] ? (
<Button className="tiny"
onClick={() => this.tradeAsset(ridge, false)}>
<FontAwesomeIcon icon={faArrowUp} /> {ridgeNames[idx]} {'('}{player.ridges[ridge]} cows{')'}
</Button>
) : (<></>)}
{' '}
</Fragment>
))}
{this.state.sendCards.map((card, idx) => (
<Button className="tiny"
key={idx}
onClick={() => this.unTradeCard(card.id)}>
{card.summary}
</Button>
))}
</Col>
</Row>
<Row>
<Col width="12">
<div className="trade-player-container">
{otherPlayer ? (
<div className='resource-unit-container'>
{this.resources.map((o, i) => {
let amount = o.key === 'money' ? 99 : otherPlayer.assets[o.key];
if (o.key === 'cows') { amount = otherPlayer.assets.cows - playerRidgeCows(otherPlayer); }
return (
<ResourceUnit img={o.img ? o.img : false} h={o.h} s={o.s} label={o.label}
key={i}
className={o.key === 'money' ? 'double-width' : false}
amount={amount - Math.max(0, toTrade[o.key])}
>
{o.icon ? (
<>
<FontAwesomeIcon icon={o.icon} />(K)
<br />
</>
) : ''}
{amount - Math.max(0, toTrade[o.key])}
{o.icon ? (<br />) : ''}
{!tradeProposed && ((amount - Math.max(0, toTrade[o.key])) > 0 || toTrade[o.key] < 0) ? (
<>
<Button className="tiny"
onClick={() => this.tradeAsset(o.key, o.amount)}>
<FontAwesomeIcon icon={faArrowUp} />
</Button>
{o.key === 'money' && (amount - Math.min(0, toTrade[o.key])) >= 10 ? (
<Button className="tiny"
onClick={() => this.tradeAsset(o.key, o.amount * 10)}>
<FontAwesomeIcon icon={faArrowUp} />
<FontAwesomeIcon icon={faArrowUp} />
</Button>
) : ''}
</>
) : (<></>)}
</ResourceUnit>
);
})}
</div>
) : (<></>)}
{Object.keys(otherPlayer ? otherPlayer.ridges : []).map((ridge, idx) => (
<Fragment key={'other-player-trade' + ridge}>
{!tradeProposed && ((otherPlayer.ridges[ridge] > 0) && !toTrade[ridge]) ? (
<Button className="tiny"
onClick={() => this.tradeAsset(ridge, otherPlayer.ridges[ridge], true)}>
<FontAwesomeIcon icon={faArrowUp} /> {ridgeNames[idx]} {'('}{otherPlayer.ridges[ridge]} cows{')'}
</Button>
) : (<></>)}
{' '}
</Fragment>
))}
<select onChange={this.selectPlayer}
value={otherPlayer.name}>
{this.props.game.otherPlayers.map(p => (
<option key={p.player.name} value={p.player.name}>
{p.player.name}
</option>
))}
</select>
{otherPlayer && otherPlayer.revealedCards.length > 0 ? (
<a onClick={e => this.viewPlayerCards(e, otherPlayer)}>
<FontAwesomeIcon icon={faWindowRestore} /> Trade Cards
</a>
) : (<></>)}
</div>
</Col>
</Row>
<Row>
<Col width="12">
{player.trade.error ? (
<p><b>ERROR:</b> {player.trade.error}</p>
) : (<></>)}
{tradeProposed && player.trade.originator === player.name ? (
<Button onClick={submitTradeCancel}>Cancel trade</Button>
) : (<></>)}
{tradeProposed && player.trade.originator !== player.name ? (
<>
<Button onClick={submitTradeAccept}>Accept trade</Button>{' '}
<Button onClick={submitTradeCancel}>Deny trade</Button>
</>
) : (<></>)}
{!tradeProposed ? (
<Button onClick={this.propose}>Propose Trade</Button>
) : (<></>)}
</Col>
</Row>
</>)}
</GroupBox>
);
}
@@ -1340,7 +1341,7 @@ class Harvest extends React.Component {
Get ready to harvest <b>{this.props.acres}
{this.props.crop === 'cows' ? ' head of cow' : ' acres'}</b>!
</div>
{isCurrentPlayer ? (
{isCurrentPlayer || this.props.currentPlayer.ai ? (
<Button onClick={() => this.nextView('roll')}>
Roll for harvest!
</Button>
@@ -1356,7 +1357,7 @@ class Harvest extends React.Component {
view = (<Die decay={true} num={this.props.rolled} ms={2000} roll={true}
rolls={this.props.rolls}
showScreen={() => this.nextView('income')}
skip={this.props.player.name === this.props.game.currentPlayer}
skip={this.props.player.name === this.props.game.currentPlayer || this.props.currentPlayer.ai}
autoSkip={this.props.autoSkip === 'die'}
showScreenDelay={2000} />);
break;
@@ -1372,7 +1373,7 @@ class Harvest extends React.Component {
</div>
</div>
</div>
{isCurrentPlayer ? (
{isCurrentPlayer || this.props.currentPlayer.ai ? (
<div className='center spacer'>
<Button onClick={() => this.nextView('operating-expense')}>Draw Operating Expense</Button>
</div>
@@ -1389,7 +1390,7 @@ class Harvest extends React.Component {
dangerouslySetInnerHTML={{__html: this.props.contents}} />
</GroupBox>
</div>
{isCurrentPlayer ? (
{isCurrentPlayer || this.props.currentPlayer.ai ? (
<div className='center spacer'>
<Button onClick={() => this.nextView('expense-value')}>Continue</Button>
</div>
@@ -1424,7 +1425,7 @@ class Harvest extends React.Component {
</div>
</div>
</div>
{isCurrentPlayer ?
{isCurrentPlayer || this.props.currentPlayer.ai ?
(
<div className='center spacer'>
<Button onClick={this.props.nextAction}>Continue</Button>
@@ -1488,7 +1489,7 @@ class Action extends React.Component {
bertSubmit = (e) => {
e.preventDefault();
if (this.state.bertChoice === 'accept') {
if (this.state.bertChoice === 'accept' || this.props.currentPlayer.ai) {
buyUncleBert();
this.props.showNextAction();
} else if (this.state.bertChoice === 'deny') {
@@ -1550,6 +1551,11 @@ class Action extends React.Component {
</form>
</>
);
const aiButton = (
<form onSubmit={this.bertSubmit}>
<Button type="submit">Continue</Button>
</form>
);
view = (
<GroupBox title={`Uncle Bert's inheritance`}>
<div className='center'>
@@ -1567,6 +1573,7 @@ class Action extends React.Component {
<div>
{(this.props.player.name === this.props.game.currentPlayer) ?
ffButtons : (<Fragment />)}
{currentPlayer.ai ? aiButton : (<></>)}
</div>
</div>
</GroupBox>
@@ -1937,6 +1944,11 @@ class StartGame extends React.Component {
): (<></>)}
</li>
))}
<li>
<span className="add-ai" onClick={addAIPlayer}>
<FontAwesomeIcon icon={faPlusCircle} />AI
</span>
</li>
</ul>
<h4>Game Settings</h4>
<ul>

View File

@@ -31,7 +31,8 @@ import { itemCard, fateCard } from 'game.js'
export { initialize, buy, roll, endTurn, loan, trade, submitTradeAccept,
submitTradeDeny, submitTradeCancel, audit, handleMessage,
nextAction, buyUncleBert, actionsFinished, skip, endAiTurn,
startGame, readyToStart, leaveGame, kickPlayer, toggleRevealForTrade }
startGame, readyToStart, leaveGame, kickPlayer, toggleRevealForTrade,
addAIPlayer }
let store;
let movingTimer = 0;
@@ -217,6 +218,10 @@ function kickPlayer(name) {
sendCommand({ type: 'kick-player', name });
}
function addAIPlayer() {
sendCommand({ type: 'add-ai-player' });
}
function toggleRevealForTrade(id) {
sendCommand({ type: 'toggle-reveal-for-trading', id });
}