1
0
James Ketrenos 0599cf2f99 Dice rolling
working on indicating what you rolled

Signed-off-by: James Ketrenos <james.p.ketrenos@intel.com>
2020-04-10 21:39:46 -07:00

870 lines
24 KiB
JavaScript
Executable File

import React, { useState, useEffect } from "react";
const hexagonRatio = 1.1547005,
tileHeight = 0.16,
tileWidth = tileHeight * hexagonRatio,
roadSize = tileHeight * 0.5 * (5. / 8.),
settlementSize = tileHeight * 0.5 - roadSize,
diceSize = 0.05,
diceMargin = diceSize * 0.5 * Math.sqrt(2),
dice = [ {
pips: 0,
jitter: 0,
angle: 0
}, {
pips: 0,
jitter: 0,
angle: 0
} ];
const Tiles = (board) => {
const tiles = [ {
type: "wood", y: 0. / 4.
}, {
type: "wood", y: 1. / 4.
}, {
type: "wood", y: 2. / 4.
}, {
type: "wood", y: 3. / 4.
}, {
type: "wheat", y: 0. / 4.
}, {
type: "wheat", y: 1. / 4.
}, {
type: "wheat", y: 2. / 4.
}, {
type: "wheat", y: 3. / 4.
}, {
type: "stone", y: 0. / 4.
}, {
type: "stone", y: 1. / 4.
}, {
type: "stone", y: 2. / 4.
}, {
type: "sheep", y: 0. / 4.
}, {
type: "sheep", y: 1. / 4.
}, {
type: "sheep", y: 2. / 4.
}, {
type: "sheep", y: 3. / 4.
}, {
type: "brick", y: 0. / 4.
}, {
type: "brick", y: 1. / 4.
}, {
type: "brick", y: 2. / 4.
}, {
type: "robber", y: 0
} ];
[ "robber", "brick", "wood", "sheep", "stone", "wheat" ].forEach((type) => {
const image = new Image(),
file = "tiles-" + type + ".png";
tiles.forEach((tile) => {
if (tile.type == type) {
tile.image = image;
tile.x = 0;
tile.pos = { x: 0, y: 0 };
}
tile.jitter = 0;
});
image.addEventListener("load", (event) => {
console.log(`Done loading ${file}`);
window.requestAnimationFrame(board.drawFrame);
});
image.addEventListener("error", (event) => {
alert(`Error loading ${file}`);
});
image.src = `assets/gfx/${file}`;
});
return tiles;
};
const Pips = (board) => {
const image = new Image(),
file = 'pip-numbers.png';
let pips = [
{ roll: 0, pips: 0, y: 3. / 6., x: 0. / 6. },
{ roll: 5, pips: 4, y: 0. / 6., x: 0. / 6. },
{ roll: 2, pips: 1, y: 0. / 6., x: 1. / 6. },
{ roll: 6, pips: 5, y: 0. / 6., x: 2. / 6. },
{ roll: 3, pips: 2, y: 0. / 6., x: 3. / 6. },
{ roll: 8, pips: 5, y: 0. / 6., x: 4. / 6. },
{ roll: 10, pips: 3, y: 0. / 6., x: 5. / 6. },
{ roll: 9, pips: 4, y: 1. / 6., x: 0. / 6. },
{ roll: 12, pips: 1, y: 1. / 6., x: 1. / 6. },
{ roll: 11, pips: 2, y: 1. / 6., x: 2. / 6. },
{ roll: 4, pips: 3, y: 1. / 6., x: 3. / 6. },
{ roll: 8, pips: 5, y: 1. / 6., x: 4. / 6. },
{ roll: 10, pips: 3, y: 1. / 6., x: 5. / 6. },
{ roll: 9, pips: 4, y: 2. / 6., x: 0. / 6. },
{ roll: 4, pips: 3, y: 2. / 6., x: 1. / 6. },
{ roll: 5, pips: 4, y: 2. / 6., x: 2. / 6. },
{ roll: 6, pips: 6, y: 2. / 6., x: 3. / 6. },
{ roll: 3, pips: 2, y: 2. / 6., x: 4. / 6. },
{ roll: 11, pips: 2, y: 2. / 6., x: 5. / 6. }
];
image.addEventListener("load", (event) => {
console.log(`Done loading ${file}`);
window.requestAnimationFrame(board.drawFrame);
});
image.addEventListener("error", (event) => {
alert(`Error loading ${file}`);
});
image.src = `assets/gfx/${file}`;
return {
image: image,
pips: pips
};
};
const Border = (board, border) => {
const image = new Image(), file = border.file
border.image = image;
image.addEventListener("load", (event) => {
console.log(`Done loading ${file}`);
window.requestAnimationFrame(board.drawFrame);
});
image.addEventListener("error", (event) => {
alert(`Error loading ${file}`);
});
image.src = `assets/gfx/${file}`;
return border;
};
const Table = (board) => {
const image = new Image(), file = "table.png";
image.addEventListener("load", (event) => {
console.log(`Done loading ${file}`);
window.requestAnimationFrame(board.drawFrame);
});
image.addEventListener("error", (event) => {
alert(`Error loading ${file}`);
});
image.src = `assets/gfx/${file}`;
return image;
};
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
class Placard extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="Placard"
style={{backgroundImage:`url(assets/gfx/placard-${this.props.type}.png)`}}>
</div>
);
}
};
class Development extends React.Component {
constructor(props) {
super(props);
}
render() {
const array = [];
for (let i = 0; i < this.props.count; i++) {
if (this.props.type.match(/-$/)) {
array.push(i + 1);//Math.ceil(Math.random() * this.props.max));
} else {
array.push("");
}
}
return (
<div className="Stack">
{ React.Children.map(array, i => (
<div className="Development"
style={{backgroundImage:`url(assets/gfx/card-${this.props.type}${i}.png)`}}>
</div>
)) }
</div>
);
}
};
class Resource extends React.Component {
constructor(props) {
super(props);
}
render() {
const array = new Array(Number(this.props.count ? this.props.count : 0));
return (
<>
{ array.length > 0 &&
<div className="Stack">
{ React.Children.map(array, i => (
<div className="Resource"
style={{backgroundImage:`url(assets/gfx/card-${this.props.type}.png)`}}>
</div>
)) }
</div>
}
</>
);
}
};
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
total: 0,
wood: 0,
sheep: 0,
brick: 0,
stone: 0,
wheat: 0
};
this.componentDidMount = this.componentDidMount.bind(this);
this.updateDimensions = this.updateDimensions.bind(this);
this.drawFrame = this.drawFrame.bind(this);
this.drawBorders = this.drawBorders.bind(this);
this.drawPips = this.drawPips.bind(this);
this.drawDie = this.drawDie.bind(this);
this.keyUp = this.keyUp.bind(this);
this.mouseMove = this.mouseMove.bind(this);
this.randomize = this.randomize.bind(this);
this.throwDice = this.throwDice.bind(this);
this.mouse = { x: 0, y: 0 };
this.radius = 0.317;
this.pips = Pips(this);
this.tiles = Tiles(this);
this.table = Table(this);
this.closest = {
info: {},
tile: null,
road: null,
tradeToken: null,
settlement: null
};
this.borders = [ {
file: 'borders-1.6.png', left: "sheep", right: "bank"
}, {
file: 'borders-2.1.png', center: "sheep"
}, {
file: 'borders-3.2.png', left: "wheat", right: "bank"
}, {
file: 'borders-4.3.png', center: "wood"
}, {
file: 'borders-5.4.png', left: "sheep", right: "bank"
}, {
file: 'borders-6.5.png', center: "bank"
} ].map((file) => {
return Border(this, file);
});
this.randomize();
}
randomize() {
//this.borders = shuffle(this.borders);
this.tiles = shuffle(this.tiles);
this.tiles.forEach((tile) => {
tile.roads = [];
tile.settlements = [];
tile.jitter = Math.random() - 0.5;
});
this.closest.tile = null;
window.requestAnimationFrame(this.drawFrame);
}
keyUp(event) {
if (event.keyCode == 78) { /* n */
this.randomize();
return;
}
if (event.keyCode == 32) { /* space */
this.throwDice();
return;
}
}
throwDice() {
for (let i = 0; i < 2; i++) {
dice[i] = {
pips: Math.ceil(Math.random() * 6),
angle: Math.random() * Math.PI * 2,
jitter: (Math.random() - 0.5) * diceSize * 0.125
}
}
window.requestAnimationFrame(this.drawFrame);
const sum = dice[0].pips + dice[1].pips;
if (sum == 7) { /* Robber! */
if (this.state.total > 7) {
let half = Math.ceil(this.state.total * 0.5);
this.state.total -= half;
while (half) {
switch (Math.floor(Math.random() * 5)) {
case 0: if (this.state.wood) { this.state.wood--; half--; } break;
case 1: if (this.state.sheep) { this.state.sheep--; half--; } break;
case 2: if (this.state.stone) { this.state.stone--; half--; } break;
case 3: if (this.state.brick) { this.state.brick--; half--; } break;
case 4: if (this.state.wheat) { this.state.wheat--; half--; } break;
}
}
}
} else {
this.tiles.forEach((tile) => {
if (tile.pip.roll != sum) {
return;
}
this.state[tile.type]++;
this.state.total++;
});
}
this.setState({
total: this.state.total,
wood: this.state.wood,
sheep: this.state.sheep,
stone: this.state.stone,
brick: this.state.brick,
wheat: this.state.wheat
});
}
mouseMove(event) {
const rect = this.canvas.parentElement.getBoundingClientRect();
let x, y;
if (event.changedTouches && event.changedTouches.length > 0) {
x = event.changedTouches[0].clientX;
y = event.changedTouches[0].clientY;
} else {
x = event.clientX;
y = event.clientY;
}
/* Scale mouse.x and mouse.y relative to board */
this.mouse.x = (x - rect.left) /
(this.minSize / hexagonRatio) - 0.5 - tileHeight * 0.5,
this.mouse.y = (y - rect.top) /
(this.minSize / hexagonRatio) - 0.5 - tileHeight * 0.5;
/* Hide the mouse cursor circle after 0.5s */
if (this.mouse.timer) {
window.clearTimeout(this.mouse.timer);
}
this.mouse.timer = window.setTimeout(() => {
this.mouse.timer = null;
window.requestAnimationFrame(this.drawFrame);
}, 500);
let closest = null;
this.tiles.forEach((tile) => {
const dX = tile.pos.x - this.mouse.x,
dY = tile.pos.y - this.mouse.y;
const distance = Math.sqrt(dX * dX + dY * dY);
if (distance > tileHeight * 0.75) {
return;
}
if (!closest || closest.distance > distance) {
closest = {
tile: tile,
distance: distance,
angle: (distance != 0.0) ? Math.atan2(dY, dX) : 0
}
}
});
if (!closest) {
this.closest.tile = null;
this.closest.info.distance = -1;
this.closest.road = null;
this.closest.angle = 0;
this.closest.settlement = null;
this.closest.tradeToken = null;
} else {
if (this.closest.tile != closest.tile) {
this.closest.tile = closest.tile;
}
this.closest.info.distance = closest.distance,
this.closest.info.angle = closest.angle;
}
window.requestAnimationFrame(this.drawFrame);
}
updateDimensions() {
if (this.updateSizeTimer) {
clearTimeout(this.updateSizeTimer);
}
this.updateSizeTimer = setTimeout(() => {
this.canvas.width = this.canvas.offsetWidth;
this.canvas.height = this.canvas.offsetHeight;
this.width = this.canvas.parentElement.clientWidth;
this.height = this.canvas.parentElement.clientHeight;
this.updateSizeTimer = 0;
this.drawFrame();
}, 250);
}
drawFrame() {
if (!this.canvas) {
return;
}
const ctx = this.canvas.getContext("2d");
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
ctx.save();
ctx.strokeStyle = 'white';
ctx.filleStyle = 'rgba(0, 0, 0, 0)';
this.minSize = Math.min(this.canvas.height, this.canvas.width);
/*
* Table tiling:
* Image width: 1080
* Left start: 32
* Right edge: 1010 (1010 - 32 = 978)
*
* If the view is wider than taller, then
*/
const tableLeft = 32 * this.table.width / 1080,
tableRight = 1010 * this.table.width / 1080,
tableLeaf = 978 * this.table.width / 1080;
/* If view is taller than wide, tile the table vertically */
ctx.save();
if (this.canvas.height > this.canvas.width) {
const tableHeight = this.canvas.width * this.table.height / this.table.width;
for (let top = 0, step = 0; top < this.canvas.height; top += tableHeight, step++) {
if (step % 2) {
ctx.save();
ctx.translate(0, tableHeight - 1);
ctx.transform(1, 0, 0, -1, 0, 0);
ctx.drawImage(this.table,
0, 0,
this.table.width, this.table.height,
0, 0, this.canvas.width, this.canvas.width * this.table.height / this.table.width);
ctx.restore();
} else {
ctx.drawImage(this.table,
0, 0,
this.table.width, this.table.height,
0, 0,
this.canvas.width, this.canvas.width * this.table.height / this.table.width);
}
ctx.translate(0, tableHeight);
}
} else {
const tableWidth = this.canvas.height * this.table.width / this.table.height;
ctx.drawImage(this.table,
0, 0,
tableRight, this.table.height,
0, 0,
this.canvas.height * tableRight / this.table.height, this.canvas.height);
let left = this.canvas.height * tableRight / this.table.height;
while (left < this.canvas.width) {
ctx.drawImage(this.table,
tableLeft, 0,
tableLeaf, this.table.height,
left, 0,
this.canvas.height * tableLeaf / this.table.height, this.canvas.height);
left += this.canvas.height * tableLeaf / this.table.height;
}
}
ctx.restore();
ctx.scale(this.minSize / hexagonRatio, this.minSize / hexagonRatio);
ctx.translate(0.5 * hexagonRatio, 0.5 * hexagonRatio);
ctx.lineWidth = 2. / this.minSize;
/* Board dimensions:
* ________
* /___1__| \
* / / \6\
* /2/ \ \
* / / \/\
* \/\ / /
* \ \ /5/
* \3\______/_/
* \_|__4___/
* 0 0.3 0.6 1
*/
ctx.save();
this.drawBorders(ctx);
ctx.restore();
ctx.save();
this.drawPips(ctx);
ctx.restore();
if (this.closest.tile) {
ctx.save();
ctx.translate(this.closest.tile.pos.x, this.closest.tile.pos.y);
ctx.strokeStyle = "red";
ctx.beginPath();
ctx.arc(0, 0, tileHeight * 0.5, 0, Math.PI * 2.);
ctx.stroke();
/* road */
let angle = Math.round(this.closest.info.angle / (Math.PI / 3.)) * (Math.PI / 3.);
ctx.strokeStyle = "rgb(64, 64, 255)";
ctx.fillStyle = "rgb(0, 0, 255)";
ctx.rotate(angle);
ctx.translate(-tileHeight * 0.5, 0);
ctx.beginPath();
ctx.rect(-roadSize * 0.125, -roadSize * 0.5, roadSize * 0.25, roadSize);
ctx.fill();
ctx.stroke();
ctx.translate(tileHeight * 0.5, 0);
ctx.rotate(-angle);
/* village */
angle = (this.closest.info.angle - Math.PI / 6.);
angle = Math.round(angle / (Math.PI / 3.)) * (Math.PI / 3.);
angle += Math.PI / 6.;
ctx.strokeStyle = "rgb(64, 64, 255)";
ctx.fillStyle = "rgb(0, 0, 255)";
ctx.rotate(angle);
ctx.translate(-tileWidth * 0.5, 0);
ctx.rotate(-angle);
ctx.beginPath();
ctx.rect(-settlementSize * 0.5, -settlementSize * 0.5, settlementSize, settlementSize);
ctx.fill();
ctx.stroke();
ctx.rotate(angle);
ctx.translate(+tileWidth * 0.5, 0);
ctx.rotate(-angle);
ctx.restore();
}
/* For 0.5 after mouse movement, there is an on
* screen mouse helper. */
if (this.mouse.timer) {
ctx.strokeStyle = "rgba(0, 255, 255)";
ctx.fillStyle = "rgba(0, 255, 255, 0.25)";
ctx.beginPath();
ctx.arc(this.mouse.x, this.mouse.y,
tileHeight * 0.5, 0, Math.PI * 2.);
ctx.stroke();
ctx.fill();
}
ctx.save();
{
ctx.translate(tileWidth * -2.5, -tileWidth * 2);
ctx.rotate(Math.PI * -0.25)
if (dice[0].pips) {
ctx.translate(-0.5 * (diceSize + diceMargin), 0);
this.drawDie(ctx, dice[0]);
}
if (dice[1].pips) {
ctx.translate(diceSize + diceMargin, 0);
this.drawDie(ctx, dice[1]);
}
}
ctx.restore();
ctx.restore();
}
drawDie(ctx, die) {
const radius = diceSize * 0.125,
offset = diceSize * 0.5 - radius,
pips = die.pips;
ctx.save();
ctx.rotate(die.angle);
ctx.translate(die.jitter, 0);
ctx.strokeStyle = "#666";
ctx.fillStyle = "#444";
ctx.beginPath();
ctx.arc(-offset, -offset, radius, Math.PI, Math.PI * 1.5);
ctx.arc(+offset, -offset, radius, Math.PI * 1.5, 0);
ctx.arc(+offset, +offset, radius, 0, Math.PI * 0.5);
ctx.arc(-offset, +offset, radius, Math.PI * 0.5, Math.PI);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.strokeStyle = "#bbb";
ctx.fillStyle = "#ddd";
/* center pip */
if (pips == 1 || pips == 3 || pips == 5) {
ctx.beginPath();
ctx.arc(0, 0, diceSize * 0.0625, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
}
/* upper left pip */
if (pips == 2 || pips == 3 || pips == 4 || pips == 5 || pips == 6) {
ctx.beginPath();
ctx.arc(-diceSize * 0.250, -diceSize * 0.250, diceSize * 0.0625, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
}
/* upper right pip */
if (pips == 4 || pips == 5 || pips == 6) {
ctx.beginPath();
ctx.arc(+diceSize * 0.250, -diceSize * 0.250, diceSize * 0.0625, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
}
/* lower right pip */
if (pips == 2 || pips == 3 || pips == 4 || pips == 5 || pips == 6) {
ctx.beginPath();
ctx.arc(+diceSize * 0.250, +diceSize * 0.250, diceSize * 0.0625, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
}
/* lower left pip */
if (pips == 4 || pips == 5 || pips == 6) {
ctx.beginPath();
ctx.arc(-diceSize * 0.250, +diceSize * 0.250, diceSize * 0.0625, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
}
/* middle left and right pip */
if (pips == 6) {
ctx.beginPath();
ctx.arc(-diceSize * 0.250, 0, diceSize * 0.0625, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(+diceSize * 0.250, 0, diceSize * 0.0625, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
}
ctx.restore();
}
drawPips(ctx) {
const image = this.pips.image, pipSize = 0.06;
function drawTile(tile, angle, radius) {
tile.pos.x = Math.sin(angle) * radius;
tile.pos.y = Math.cos(angle) * radius;
const image = tile.image;
ctx.save();
ctx.rotate(angle);
ctx.translate(0., radius);
ctx.rotate(-angle + Math.PI * 1. / 6.);
ctx.drawImage(image,
tile.x * image.width, tile.y * image.height,
image.width, image.height / 4.,
-tileWidth * 0.5, -tileHeight * 0.5,
tileWidth, tileHeight);
ctx.restore();
}
function drawPip(pip, angle, radius, jitter) {
ctx.save();
ctx.rotate(angle);
ctx.translate(0., radius);
/* Offset into a random direction by a random amount */
ctx.rotate(Math.PI * 2. * jitter);
ctx.translate(0, tileHeight * 0.1 * jitter);
/* Undo random rotation for position, and add random rotation
* for pip placement */
ctx.rotate(-angle - Math.PI * 2. * jitter + jitter * 0.4);
ctx.drawImage(image,
pip.x * image.width, pip.y * image.height,
image.width / 6., image.height / 6.,
-pipSize * 0.5, -pipSize * 0.5,
pipSize, pipSize);
ctx.restore();
}
let angle,
radius = this.radius,
index = 1, pip, roll = dice[0].pips + dice[1].pips;
/* Outer row */
angle = 0;
for (let i = 0; i < 12; i++) {
angle -= Math.PI * 2. / 12.;
if (this.tiles[i].type == "robber") {
pip = this.pips.pips[0]
} else {
pip = this.pips.pips[index++];
}
this.tiles[i].pip = pip;
drawTile(this.tiles[i], angle, radius - (i % 2) * 0.04);
if (roll == pip.roll) {
ctx.scale(1.25, 1.25);
}
drawPip(pip, angle, radius - (i % 2) * 0.04, this.tiles[i].jitter);
if (roll == pip.roll) {
ctx.scale(0.8, 0.8);
}
}
/* Middle row */
angle = Math.PI * 2. / 12.;
radius = this.radius * 0.5;
for (let i = 12; i < 18; i++) {
angle -= Math.PI * 2. / 6.;
if (this.tiles[i].type == "robber") {
pip = this.pips.pips[0]
} else {
pip = this.pips.pips[index++];
}
this.tiles[i].pip = pip;
drawTile(this.tiles[i], angle, radius);
if (roll == pip.roll) {
ctx.scale(1.25, 1.25);
}
drawPip(pip, angle, radius, this.tiles[i].jitter);
if (roll == pip.roll) {
ctx.scale(0.8, 0.8);
}
}
/* Center */
let i = 18;
if (this.tiles[i].type == "robber") {
pip = this.pips.pips[0]
} else {
pip = this.pips.pips[index++];
}
this.tiles[i].pip = pip;
drawTile(this.tiles[i], 0, 0);
if (roll == pip.roll) {
ctx.scale(1.25, 1.25);
}
drawPip(pip, 0, 0, this.tiles[i].jitter);
if (roll == pip.roll) {
ctx.scale(0.8, 0.8);
}
}
drawBorders(ctx) {
ctx.rotate(Math.PI);
const offset = 0.18;
this.borders.forEach((border, index) => {
ctx.translate(0, this.radius);
const image = border.image;
ctx.drawImage(image,
-offset, 0,
0.5, 0.5 * image.height / image.width);
ctx.translate(0, -this.radius);
ctx.rotate(Math.PI * 2. / 6.);
});
}
componentDidMount() {
this.start = new Date();
document.addEventListener("keyup", this.keyUp);
window.addEventListener("touchmove", this.mouseMove);
window.addEventListener("mousemove", this.mouseMove);
window.addEventListener("resize", this.updateDimensions);
this.updateDimensions();
}
componentWillUnmount() {
if (this.updateSizeTimer) {
clearTimeout(this.updateSizeTimer);
this.updateSizeTimer = 0;
}
document.removeEventListener("keyup", this.keyUp);
window.removeEventListener("mousemove", this.mouseMove);
window.removeEventListener("touchmove", this.mouseMove);
window.removeEventListener("resize", this.updateDimensions);
}
render() {
return (
<>
<canvas className="Board" ref={el => this.canvas = el}></canvas>
<div className="Cards">
<div>In hand</div>
<div className="Hand">
<Resource type="wood" count={this.state.wood}/>
<Resource type="wheat" count={this.state.wheat}/>
<Resource type="stone" count={this.state.stone}/>
<Resource type="brick" count={this.state.brick}/>
<Resource type="sheep" count={this.state.sheep}/>
</div>
<div>Available to play</div>
<div className="Hand">
<Development type="monopoly" count="1"/>
<Development type="army-" max="14" count="4"/>
<div className="Stack">
<Development type="vp-library" count="1"/>
<Development type="vp-market" count="1"/>
</div>
</div>
<div>Points</div>
<div className="Hand">
<div className="Stack">
<Development type="vp-library" count="1"/>
<Development type="vp-palace" count="1"/>
<Development type="army-" max="14" count="6"/>
</div>
</div>
<div className="Hand">
<Placard type="largest-army" count="1"/>
<Placard type="longest-road" count="1"/>
</div>
<div className="Statistics">
<div>Stats</div>
<div>
<div>Points: 7</div>
<div>Cards: {this.state.total} </div>
<div>Roads remaining: 4</div>
<div>Longest road: 7</div>
<div>Cities remaining: 4</div>
<div>Settlements remaining: 5</div>
</div>
</div>
</div>
</>
);
}
}
export default Board;