Longest road is now tracking
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
5cc69d04bc
commit
30bb9870da
@ -37,11 +37,11 @@
|
||||
}
|
||||
|
||||
.Pip.Active {
|
||||
filter: drop-shadow(0px 0px 5px rgba(255, 255, 0, 0.9));
|
||||
filter: drop-shadow(0px 0px 10px rgba(255, 0, 255, 1));
|
||||
}
|
||||
|
||||
.Pip.Active.Option {
|
||||
filter: brightness(150%) drop-shadow(0px 0px 5px rgba(255, 255, 0, 0.9));
|
||||
filter: brightness(150%) drop-shadow(0px 0px 10px rgba(255, 0, 255, 1));
|
||||
}
|
||||
|
||||
.Pips[disabled],
|
||||
|
@ -48,8 +48,10 @@ const Placard = ({table, type, active}) => {
|
||||
}
|
||||
|
||||
const buildClicked = (event) => {
|
||||
if (!table.state.buildActive) {
|
||||
table.setState({ buildActive: true });
|
||||
if (!type.match(/^l.*/)) {
|
||||
if (!table.state.buildActive) {
|
||||
table.setState({ buildActive: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -1004,6 +1006,13 @@ class Table extends React.Component {
|
||||
<div className="Hand">
|
||||
{ development }
|
||||
</div>
|
||||
{ game.longestRoad && game.longestRoad === game.color &&
|
||||
<Placard
|
||||
active={false}
|
||||
type='longest-road'
|
||||
table={this}
|
||||
/>
|
||||
}
|
||||
<Placard
|
||||
active={this.state.buildActive}
|
||||
disabled={!game || !game.turn || !game.turn.roll}
|
||||
@ -1042,36 +1051,6 @@ class Table extends React.Component {
|
||||
(!game.player || !game.player.mustDiscard) && <WaitingForPlayer table={this}/>
|
||||
}
|
||||
|
||||
{ game && game.showCards &&
|
||||
<div className="Cards">
|
||||
{ game && game.state === "active" && <>
|
||||
<div>In hand</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"/>
|
||||
<Placard type="longest-road"/>
|
||||
</div>
|
||||
</> }
|
||||
</div>
|
||||
}
|
||||
|
||||
{ this.state.error && <Paper className="Error"><div>{this.state.error}</div></Paper> }
|
||||
|
||||
</div>
|
||||
|
@ -805,6 +805,185 @@ const getPrevPlayer = (game, name) => {
|
||||
return name;
|
||||
}
|
||||
|
||||
const processCorner = (game, color, cornerIndex, placedCorner) => {
|
||||
/* If this corner is allocated and isn't assigned to the walking color, skip it */
|
||||
if (placedCorner.color && placedCorner.color !== color) {
|
||||
return -1;
|
||||
}
|
||||
/* If this corner is already being walked, skip it */
|
||||
if (placedCorner.walking) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
placedCorner.walking = true;
|
||||
/* Calculate the longest road branching from both corners */
|
||||
let longest = 0;
|
||||
layout.corners[cornerIndex].roads.forEach(roadIndex => {
|
||||
const placedRoad = game.placements.roads[roadIndex];
|
||||
longest = Math.max(processRoad(game, color, roadIndex, placedRoad), longest);
|
||||
});
|
||||
|
||||
return longest;
|
||||
};
|
||||
|
||||
const buildCornerGraph = (game, color, cornerIndex, placedCorner, set) => {
|
||||
/* If this corner is allocated and isn't assigned to the walking color, skip it */
|
||||
if (placedCorner.color && placedCorner.color !== color) {
|
||||
return;
|
||||
}
|
||||
/* If this corner is already being walked, skip it */
|
||||
if (placedCorner.walking) {
|
||||
return;
|
||||
}
|
||||
|
||||
placedCorner.walking = true;
|
||||
/* Calculate the longest road branching from both corners */
|
||||
layout.corners[cornerIndex].roads.forEach(roadIndex => {
|
||||
const placedRoad = game.placements.roads[roadIndex];
|
||||
buildRoadGraph(game, color, roadIndex, placedRoad, set);
|
||||
});
|
||||
};
|
||||
|
||||
const processRoad = (game, color, roadIndex, placedRoad) => {
|
||||
/* If this road isn't assigned to the walking color, skip it */
|
||||
if (placedRoad.color !== color) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If this road is already being walked, skip it */
|
||||
if (placedRoad.walking) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
placedRoad.walking = true;
|
||||
/* Calculate the longest road branching from both corners */
|
||||
let longest = 0;
|
||||
layout.roads[roadIndex].corners.forEach(cornerIndex => {
|
||||
const placedCorner = game.placements.corners[cornerIndex];
|
||||
longest = Math.max(processCorner(game, color, cornerIndex, placedCorner), longest);
|
||||
});
|
||||
placedRoad.longest = 1 + longest;
|
||||
|
||||
return placedRoad.longest;
|
||||
};
|
||||
|
||||
const buildRoadGraph = (game, color, roadIndex, placedRoad, set) => {
|
||||
/* If this road isn't assigned to the walking color, skip it */
|
||||
if (placedRoad.color !== color) {
|
||||
return;
|
||||
}
|
||||
/* If this road is already being walked, skip it */
|
||||
if (placedRoad.walking) {
|
||||
return;
|
||||
}
|
||||
|
||||
placedRoad.walking = true;
|
||||
set.push(roadIndex);
|
||||
/* Calculate the longest road branching from both corners */
|
||||
layout.roads[roadIndex].corners.forEach(cornerIndex => {
|
||||
const placedCorner = game.placements.corners[cornerIndex];
|
||||
buildCornerGraph(game, color, cornerIndex, placedCorner, set)
|
||||
});
|
||||
};
|
||||
|
||||
const clearRoadMarkers = (game) => {
|
||||
/* Clear out walk markers on roads */
|
||||
layout.roads.forEach((item, itemIndex) => {
|
||||
const placed = game.placements.roads[itemIndex];
|
||||
placed.walking = false;
|
||||
placed.longest = 0;
|
||||
});
|
||||
|
||||
/* Clear out walk markers on corners */
|
||||
layout.corners.forEach((item, itemIndex) => {
|
||||
const placed = game.placements.corners[itemIndex];
|
||||
placed.walking = false;
|
||||
});
|
||||
}
|
||||
|
||||
const calculateRoadLengths = (game, session) => {
|
||||
clearRoadMarkers(game);
|
||||
|
||||
/* Clear out player longest road counts */
|
||||
for (let key in game.players) {
|
||||
game.players[key].roadLength = 0;
|
||||
}
|
||||
|
||||
/* Build a set of connected road graphs. Once all graphs are
|
||||
* constructed, walk through each graph, starting from each
|
||||
* location in the graph. If the length ever equals the
|
||||
* number of items in the graph, short circuit--longest path.
|
||||
* Otherwise, check all paths from each segment. This is
|
||||
* needed to catch loops where starting from an outside end
|
||||
* point may result in not counting the length of the loop
|
||||
*/
|
||||
let graphs = [];
|
||||
layout.roads.forEach((road, roadIndex) => {
|
||||
const placedRoad = game.placements.roads[roadIndex];
|
||||
if (placedRoad.color) {
|
||||
let set = [];
|
||||
buildRoadGraph(game, placedRoad.color, roadIndex, placedRoad, set);
|
||||
if (set.length) {
|
||||
graphs.push({ color: placedRoad.color, set });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(graphs);
|
||||
|
||||
let currentLength = game.longestRoad ? game.players[game.longestRoad].roadLength : -1,
|
||||
currentLongest = game.longestRoad;
|
||||
clearRoadMarkers(game);
|
||||
graphs.forEach(graph => {
|
||||
graph.set.forEach(roadIndex => {
|
||||
const placedRoad = game.placements.roads[roadIndex];
|
||||
clearRoadMarkers(game);
|
||||
const length = processRoad(game, placedRoad.color, roadIndex, placedRoad);
|
||||
game.players[placedRoad.color].roadLength =
|
||||
Math.max(game.players[placedRoad.color].roadLength, length);
|
||||
});
|
||||
});
|
||||
|
||||
const checkForTies = false;
|
||||
if (currentLongest && game.players[game.currentLongest].roadLength < currentLength) {
|
||||
addChatMessage(game, session, `${getPlayerNameFromColor(game, game.currentLongest)} had their longest road split!`);
|
||||
checkForTies = true;
|
||||
}
|
||||
|
||||
let longest = game.longestRoad ? game.players[game.longestRoad].roadLength : 4,
|
||||
longestPlayers = [];
|
||||
for (let key in game.players) {
|
||||
if (game.players[key].status === 'Not active') {
|
||||
continue;
|
||||
}
|
||||
if (game.players[key].roadLength > longest) {
|
||||
longestPlayers = [ key ];
|
||||
longest = game.players[key].roadLength;
|
||||
} else if (game.players[key].roadLength == longest && checkForTies) {
|
||||
longestPlayers.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (longestPlayers.length > 0) {
|
||||
if (longestPlayers.length === 1) {
|
||||
if (game.longestRoad !== longestPlayers[0]) {
|
||||
game.longestRoad = longestPlayers[0];
|
||||
addChatMessage(game, session, `${playerNameFromColor(game, game.longestRoad)} now has the longest road (${longest})!`);
|
||||
}
|
||||
} else {
|
||||
if (checkForTies) {
|
||||
const names = longestPlayers.map(color => playerNameFromColor(color));
|
||||
addChatMessage(game, session, `${names.join(', ')} are tied for longest road (${longest})!`);
|
||||
}
|
||||
game.longestRoad = null;
|
||||
}
|
||||
} else {
|
||||
game.longestRoad = null;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
const getValidCorners = (game, color, type) => {
|
||||
const limits = [];
|
||||
|
||||
@ -982,6 +1161,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
/* Any player can make an offer */
|
||||
if (value === 'offer') {
|
||||
const offer = req.body;
|
||||
console.log('TODO: Verify player has sufficient resources.');
|
||||
session.player.gives = offer.gives;
|
||||
session.player.gets = offer.gets;
|
||||
if (game.turn.name === name) {
|
||||
@ -1006,7 +1186,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
target = game.players[offer.color];
|
||||
offer.gives.forEach(item => {
|
||||
const isOffered = target.gives.find(
|
||||
match => match.type === item.type && match.count === item.count);
|
||||
match => match.type === item.type && match.count === item.count);
|
||||
if (!isOffered) {
|
||||
mismatch = true;
|
||||
}
|
||||
@ -1022,12 +1202,19 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
error = `Unfortunately, trades were re-negotiated in transit and the deal is invalid!`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify the requesting offer wasn't jacked */
|
||||
console.log('TODO: Verify the player trade matches the offer target');
|
||||
|
||||
/* Transfer goods */
|
||||
player.gets.forEach(item => {
|
||||
if (target) {
|
||||
target[item.type] -= item.count;
|
||||
if (target[item.type] < 0) {
|
||||
console.log(`Cheating!!!`);
|
||||
target[item.type] = 0;
|
||||
}
|
||||
}
|
||||
player[item.type] += item.count;
|
||||
});
|
||||
@ -1036,6 +1223,10 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
target[item.type] += item.count;
|
||||
}
|
||||
player[item.type] -= item.count;
|
||||
if (player[item.type] < 0) {
|
||||
console.log(`Cheating!!!`);
|
||||
player[item.type] = 0;
|
||||
}
|
||||
});
|
||||
|
||||
delete game.turn.offer;
|
||||
@ -1162,6 +1353,8 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
});
|
||||
if (cards.length === 0) {
|
||||
addChatMessage(game, session, `Victim did not have any cards to steal.`);
|
||||
game.turn.actions = [];
|
||||
game.turn.limits = {};
|
||||
} else {
|
||||
let index = Math.floor(Math.random() * cards.length),
|
||||
type = cards[index];
|
||||
@ -1299,6 +1492,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
game.turn.actions = [];
|
||||
game.turn.limits = {};
|
||||
addChatMessage(game, session, `${name} placed a settlement.`);
|
||||
calculateRoadLengths(game, session);
|
||||
} else if (game.state === 'initial-placement') {
|
||||
if (game.direction && game.direction === 'backward') {
|
||||
session.initialSettlement = index;
|
||||
@ -1340,7 +1534,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
error = `You cannot build until you have rolled.`;
|
||||
break;
|
||||
}
|
||||
if (player.wheat < 3 || player.stone < 2) {
|
||||
if (player.wheat < 2 || player.stone < 3) {
|
||||
error = `You have insufficient resources to build a city.`;
|
||||
break;
|
||||
}
|
||||
@ -1386,7 +1580,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
error = `This location already has a city!`;
|
||||
break;
|
||||
}
|
||||
if (player.wheat < 3 || player.stone < 2) {
|
||||
if (player.wheat < 2 || player.stone < 3) {
|
||||
error = `You have insufficient resources to build a city.`;
|
||||
break;
|
||||
}
|
||||
@ -1398,8 +1592,8 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
corner.type = 'city';
|
||||
player.cities--;
|
||||
player.settlements++;
|
||||
player.wheat -= 3;
|
||||
player.stone -= 2;
|
||||
player.wheat -= 2;
|
||||
player.stone -= 3;
|
||||
game.turn.actions = [];
|
||||
game.turn.limits = {};
|
||||
addChatMessage(game, session, `${name} upgraded a settlement to a city!`);
|
||||
@ -1477,9 +1671,12 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
game.turn.actions = [];
|
||||
game.turn.limits = {};
|
||||
addChatMessage(game, session, `${name} placed a road.`);
|
||||
calculateRoadLengths(game, session);
|
||||
|
||||
} else if (game.state === 'initial-placement') {
|
||||
road.color = session.color;
|
||||
addChatMessage(game, session, `${name} placed a road.`);
|
||||
calculateRoadLengths(game, session);
|
||||
|
||||
let next;
|
||||
if (game.direction === 'forward' && getLastPlayerName(game) === name) {
|
||||
@ -1502,6 +1699,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
name: next,
|
||||
color: getColorFromName(game, next)
|
||||
};
|
||||
calculateRoadLengths(game, session);
|
||||
addChatMessage(game, null, `It is ${next}'s turn. Place a settlement.`);
|
||||
} else {
|
||||
game.turn = {
|
||||
@ -1737,13 +1935,34 @@ const resetGame = (game) => {
|
||||
roads: []
|
||||
};
|
||||
|
||||
Object.assign(game, {
|
||||
sheep: 19,
|
||||
ore: 19,
|
||||
wool: 19,
|
||||
brick: 19,
|
||||
wheat: 19,
|
||||
longestRoad: null,
|
||||
largestArmy: null,
|
||||
developmentCards: assetData.developmentCards.slice()
|
||||
});
|
||||
|
||||
for (let key in game.players) {
|
||||
game.players[key].wheat =
|
||||
game.players[key].sheep =
|
||||
game.players[key].stone =
|
||||
game.players[key].brick =
|
||||
game.players[key].wood = 0;
|
||||
Object.assign(game.players[key], {
|
||||
wheat: 0,
|
||||
sheep: 0,
|
||||
stone: 0,
|
||||
brick: 0,
|
||||
wood: 0,
|
||||
roads: 15,
|
||||
cities: 4,
|
||||
settlements: 5,
|
||||
points: 0,
|
||||
development: []
|
||||
});
|
||||
}
|
||||
|
||||
game.developmentCards = assetData.developmentCards.slice();
|
||||
shuffle(game.developmentCards);
|
||||
|
||||
for (let i = 0; i < layout.corners.length; i++) {
|
||||
game.placements.corners[i] = {
|
||||
@ -1883,7 +2102,7 @@ const shuffleBoard = (game) => {
|
||||
}
|
||||
}
|
||||
|
||||
shuffle(game.developmentCards);
|
||||
shuffle(game.developmentCards);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user