1
0

Longest road calculations are now correct for each segment. Visualize longest road

Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
James Ketrenos 2022-02-20 22:52:55 -08:00
parent 27cdcda2d4
commit 433eff8473
6 changed files with 249 additions and 42 deletions

View File

@ -25,7 +25,7 @@
.Tile.Active {
z-index: 10; /* Above non-Active Tile, below Road and Corner */
filter: drop-shadow(8px 8px 12px black);
filter: drop-shadow(0px 0px 12px black);
}
.Border {
@ -59,7 +59,7 @@
}
.Corner {
filter: drop-shadow(2.5px 2.5px 1.5px rgba(0, 0, 0, 0.75));
filter: drop-shadow(0px 0px 5px rgba(0, 0, 0, 0.75));
z-index: 20; /* Above Tile, below Road */
position: absolute;
display: flex;
@ -167,7 +167,42 @@
.Road-Shape:hover {
background-color: white;
filter: brightness(150%);
}
.Road.LongestRoad .Road-Shape {
filter: brightness(135%);
transform: scale(1.05);
}
.Road[data-longest="1"],
.Road[data-longest="2"],
.Road[data-longest="3"],
.Road[data-longest="4"] {
filter: drop-shadow(0px 0px 2.5px black);
}
.Road[data-longest="5"] {
filter: drop-shadow(0px 0px 5px black);
}
.Road[data-longest="6"] {
filter: drop-shadow(0px 0px 6px black);
}
.Road[data-longest="7"] {
filter: drop-shadow(0px 0px 7px black);
}
.Road[data-longest="8"] {
filter: drop-shadow(0px 0px 8px black);
}
.Road[data-longest="9"] {
filter: drop-shadow(0px 0px 9px black);
}
.Road[data-longest="10"],
.Road[data-longest="11"],
.Road[data-longest="12"],
.Road[data-longest="13"],
.Road[data-longest="14"],
.Road[data-longest="15"] {
filter: drop-shadow(0px 0px 10px black);
}
.Board .Selected {

View File

@ -420,6 +420,16 @@ const Board = ({ table, game }) => {
if (!road.color) {
el.removeAttribute('data-color');
} else {
if (road.longestRoad) {
if (road.longestRoad === game.longestRoadLength) {
el.classList.add('LongestRoad');
} else {
el.classList.remove('LongestRoad');
}
el.setAttribute('data-longest', road.longestRoad);
} else {
el.removeAttribute('data-longest');
}
el.setAttribute('data-color', road.color);
}
});
@ -494,7 +504,9 @@ const Board = ({ table, game }) => {
return (
<div className="Board">
<div className="BoardBox">
{ borders }
<div className="Borders" disabled>
{ borders }
</div>
{ game && <>
<div className="Tiles" disabled>
{ tiles }

View File

@ -474,6 +474,9 @@ const Players = ({ table }) => {
let toggleText;
if (name) {
toggleText = `${name} has ${item.points} VP`;
if (item.unplayed) {
toggleText += ` and ${item.unplayed} unplayed DCs`;
}
} else {
toggleText = "Available";
}

View File

@ -5,6 +5,10 @@ import Button from '@material-ui/core/Button';
import Resource from './Resource.js';
const ViewCard = ({table, card}) => {
if (!table.game.player) {
return <></>;
}
const playCard = (event) => {
table.playCard(card);
}
@ -31,10 +35,30 @@ const ViewCard = ({table, card}) => {
vp: <><b>1</b> victory point.
<p>You only reveal your victory point cards when the game is over, either
when you or an opponent reaches <b>10+</b> victory points on their turn and declares
victory!</p></>
victory!</p></>,
'progress-road-2': <>
<p>Play <b>2</b> new roads as if you had just built them.</p>
<p>This is still limited by the number of roads you have (a maximum of 15.)</p>
<p><b>NOTE:</b> This card is not yet implemented. The server will give you <b>2</b> wood
and <b>2</b> brick and we trust you will use them to build <b>2</b> roads.</p>
<p>If
you do not have enough roads remaining, you may end up with extra resources...
but the game is in beta, so... be happy :)
</p>
<p>As an FYI, you currently have {15 - table.game.player.roads} roads remaining.</p>
</>
};
let description = descriptions[card.type];
let description;
if (card.type == 'progress') {
description = descriptions[`${card.type}-${card.card}`];
} else {
description = descriptions[card.type];
}
if (description === undefined) {
console.log('No description for ', card);
}
let canPlay = false;
if (card.type === 'vp') {

View File

@ -16,8 +16,17 @@ const Winner = ({table, color}) => {
const name = getPlayerName(table.game.sessions, color),
player = table.game.players[color];
let playerCount = 0;
for (let key in table.game.players) {
if (table.game.players[key].status !== 'Not active') {
playerCount++;
}
}
let description = <>Congratulations, <b>{name}</b>!</>;
let description = <>Congratulations, <b>{name}</b>!
They have won the game! The game played
for {Math.floor(table.game.turns / playerCount)} turns.
</>;
for (let key in table.game.players) {
if (key === color) {
continue;
@ -30,6 +39,8 @@ const Winner = ({table, color}) => {
description = <>{description}<p>{line}</p></>;
}
description = <>{description}<p>If everyone goes back to the Lobby, you can play again.</p></>;
return (
<div className="Winner">
<Paper>

View File

@ -7,6 +7,7 @@ const express = require("express"),
accessSync = fs.accessSync,
randomWords = require("random-words");
const { clear } = require("console");
const { corners } = require("./layout.js");
const layout = require('./layout.js');
@ -854,11 +855,11 @@ const getPrevPlayer = (game, 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;
return 0;
}
/* If this corner is already being walked, skip it */
if (placedCorner.walking) {
return -1;
return 0;
}
placedCorner.walking = true;
@ -866,7 +867,18 @@ const processCorner = (game, color, cornerIndex, placedCorner) => {
let longest = 0;
layout.corners[cornerIndex].roads.forEach(roadIndex => {
const placedRoad = game.placements.roads[roadIndex];
longest = Math.max(processRoad(game, color, roadIndex, placedRoad), longest);
if (placedRoad.walking) {
return;
}
const tmp = processRoad(game, color, roadIndex, placedRoad);
longest = Math.max(tmp, longest);
/*if (tmp > longest) {
longest = tmp;
placedCorner.longestRoad = roadIndex;
placedCorner.longest
}
longest = Math.max(
*/
});
return longest;
@ -893,24 +905,26 @@ const buildCornerGraph = (game, color, cornerIndex, placedCorner, 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;
return 0;
}
/* If this road is already being walked, skip it */
if (placedRoad.walking) {
return -1;
return 0;
}
placedRoad.walking = true;
/* Calculate the longest road branching from both corners */
let longest = 0;
let roadLength = 1;
layout.roads[roadIndex].corners.forEach(cornerIndex => {
const placedCorner = game.placements.corners[cornerIndex];
longest = Math.max(processCorner(game, color, cornerIndex, placedCorner), longest);
if (placedCorner.walking) {
return;
}
roadLength += processCorner(game, color, cornerIndex, placedCorner);
});
placedRoad.longest = 1 + longest;
return placedRoad.longest;
return roadLength;
};
const buildRoadGraph = (game, color, roadIndex, placedRoad, set) => {
@ -932,27 +946,29 @@ const buildRoadGraph = (game, color, roadIndex, placedRoad, set) => {
});
};
const clearRoadMarkers = (game) => {
const clearRoadWalking = (game) => {
/* Clear out walk markers on roads */
layout.roads.forEach((item, itemIndex) => {
const placed = game.placements.roads[itemIndex];
placed.walking = false;
placed.longest = 0;
delete game.placements.roads[itemIndex].walking;
});
/* Clear out walk markers on corners */
layout.corners.forEach((item, itemIndex) => {
const placed = game.placements.corners[itemIndex];
placed.walking = false;
delete game.placements.corners[itemIndex].walking;
});
}
const calculateRoadLengths = (game, session) => {
clearRoadMarkers(game);
clearRoadWalking(game);
let currentLongest = game.longestRoad,
currentLength = currentLongest
? game.players[currentLongest].longestRoad
: -1;
/* Clear out player longest road counts */
for (let key in game.players) {
game.players[key].roadLength = 0;
game.players[key].length = 0;
}
/* Build a set of connected road graphs. Once all graphs are
@ -975,50 +991,87 @@ const calculateRoadLengths = (game, session) => {
}
});
console.log(graphs);
console.log('Graphs A:', graphs);
clearRoadWalking(game);
graphs.forEach(graph => {
graph.longestRoad = 0;
graph.set.forEach(roadIndex => {
const placedRoad = game.placements.roads[roadIndex];
clearRoadWalking(game);
const length = processRoad(game, placedRoad.color, roadIndex, placedRoad);
if (length >= graph.longestRoad) {
graph.longestStartSegment = roadIndex;
graph.longestRoad = length;
}
});
});
console.log('Graphs B:', graphs);
console.log('Pre update:', game.placements.roads.filter(road => road.color));
for (let color in game.players) {
if (game.players[color] === 'Not active') {
continue;
}
game.players[color].longestRoad = 0;
}
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);
clearRoadWalking(game);
const longestRoad = processRoad(game, placedRoad.color, roadIndex, placedRoad);
placedRoad.longestRoad = longestRoad;
game.players[placedRoad.color].longestRoad =
Math.max(game.players[placedRoad.color].longestRoad, longestRoad);
});
});
game.placements.roads.forEach(road => delete road.walking);
console.log('Post update:', game.placements.roads.filter(road => road.color));
let checkForTies = false;
const checkForTies = false;
if (currentLongest && game.players[currentLongest].roadLength < currentLength) {
addChatMessage(game, session, `${getPlayerNameFromColor(game, game.currentLongest)} had their longest road split!`);
console.log(currentLongest, currentLength);
if (currentLongest && game.players[currentLongest].longestRoad < currentLength) {
addChatMessage(game, session, `${playerNameFromColor(game, currentLongest)} had their longest road split!`);
checkForTies = true;
}
let longest = 4, longestPlayers = [];
let longestRoad = 4, longestPlayers = [];
for (let key in game.players) {
if (game.players[key].status === 'Not active') {
continue;
}
if (game.players[key].roadLength > longest) {
if (game.players[key].longestRoad > longestRoad) {
longestPlayers = [ key ];
longest = game.players[key].roadLength;
} else if (game.players[key].roadLength == longest && checkForTies) {
longestRoad = game.players[key].longestRoad;
} else if (game.players[key].longestRoad == longestRoad && checkForTies) {
longestPlayers.push(key);
}
}
console.log({ longestPlayers });
if (longestPlayers.length > 0) {
if (longestPlayers.length === 1) {
game.longestRoadLength = longestRoad;
if (game.longestRoad !== longestPlayers[0]) {
game.longestRoad = longestPlayers[0];
addChatMessage(game, session, `${playerNameFromColor(game, game.longestRoad)} now has the longest road (${longest})!`);
addChatMessage(game, session,
`${playerNameFromColor(game, game.longestRoad)} now has the longest ` +
`road (${longestRoad})!`);
}
} else {
if (checkForTies) {
const names = longestPlayers.map(color => playerNameFromColor(color));
addChatMessage(game, session, `${names.join(', ')} are tied for longest road (${longest})!`);
game.longestRoadLength = longestRoad;
const names = longestPlayers.map(color => playerNameFromColor(game, color));
addChatMessage(game, session, `${names.join(', ')} are tied for longest ` +
`road (${longestRoad})!`);
}
game.longestRoad = null;
}
@ -1399,6 +1452,8 @@ router.put("/:id/:action/:value?", async (req, res) => {
break;
}
debugChat(game, 'Before trade');
/* Transfer goods */
player.gets.forEach(item => {
if (target.name !== 'The bank') {
@ -1425,6 +1480,8 @@ router.put("/:id/:action/:value?", async (req, res) => {
delete session.player.gives;
delete session.player.gets;
debugChat(game, 'After trade');
game.turn.actions = [];
break;
@ -1538,6 +1595,9 @@ router.put("/:id/:action/:value?", async (req, res) => {
cards.push(field);
}
});
debugChat(game, 'Before steal');
if (cards.length === 0) {
addChatMessage(game, session, `${playerNameFromColor(game, value)} did not have any cards to steal.`);
game.turn.actions = [];
@ -1552,6 +1612,8 @@ router.put("/:id/:action/:value?", async (req, res) => {
addChatMessage(game, session,
`${session.name} randomly stole ${type} from ${playerNameFromColor(game, value)}.`);
}
debugChat(game, 'After steal');
game.turn.robberInAction = false;
break;
@ -1585,10 +1647,13 @@ router.put("/:id/:action/:value?", async (req, res) => {
if (game.turn.developmentPurchased) {
error = `You have already purchased a development card this turn.`;
}
debugChat(game, 'Before development purchase');
addChatMessage(game, session, `${session.name} purchased a development card.`);
player.stone--;
player.wheat--;
player.sheep--;
debugChat(game, 'After development purchase');
card = game.developmentCards.pop();
card.turn = game.turns;
player.development.push(card);
@ -1644,6 +1709,11 @@ router.put("/:id/:action/:value?", async (req, res) => {
}
}
if (card.type === 'progress' && card.card === 'road-2') {
addChatMessage(game, session, `${session.name} played a Road Building card. The server is giving them 2 brick and 2 wood to build those roads!`);
player.brick += 2;
player.wood += 2;
}
card.played = true;
player.playedCard = game.turns;
addChatMessage(game, session, `${session.name} played a ${card.type}-${card.card} development card.`);
@ -1741,11 +1811,14 @@ router.put("/:id/:action/:value?", async (req, res) => {
error = `You have already built all of your settlements.`;
break;
}
debugChat(game, 'Before settlement purchase');
player.settlements--;
player.brick--;
player.wood--;
player.wheat--;
player.sheep--;
debugChat(game, 'After settlement purchase');
corner.color = session.color;
corner.type = 'settlement';
if (layout.corners[index].banks.length) {
@ -1870,10 +1943,13 @@ router.put("/:id/:action/:value?", async (req, res) => {
}
corner.color = session.color;
corner.type = 'city';
debugChat(game, 'Before city purchase');
player.cities--;
player.settlements++;
player.wheat -= 2;
player.stone -= 3;
debugChat(game, 'After city purchase');
game.turn.actions = [];
game.turn.limits = {};
addChatMessage(game, session, `${name} upgraded a settlement to a city!`);
@ -1950,9 +2026,13 @@ router.put("/:id/:action/:value?", async (req, res) => {
error = `You have already built all of your roads.`;
break;
}
debugChat(game, 'Before road purchase');
player.roads--;
player.brick--;
player.wood--;
debugChat(game, 'After road purchase');
road.color = session.color;
game.turn.actions = [];
game.turn.limits = {};
@ -2101,6 +2181,36 @@ router.get("/:id", async (req, res/*, next*/) => {
return sendGame(req, res, game);
});
const debugChat = (game, preamble) => {
preamble = `Degug ${preamble.trim()}`;
let playerInventory = preamble;
for (let key in game.players) {
if (game.players[key].status === 'Not active') {
continue;
}
if (playerInventory !== '') {
playerInventory += ' player';
} else {
playerInventory += ' Player'
}
playerInventory += ` ${playerNameFromColor(game, key)} has `;
const has = [ 'wheat', 'brick', 'sheep', 'stone', 'wood' ].map(resource => {
if (game.players[key][resource] > 0) {
return `${game.players[key][resource]} ${resource}`;
}
return '';
}).filter(item => item !== '').join(', ');
if (has) {
playerInventory += `${has}, `;
} else {
playerInventory += `nothing, `;
}
}
addChatMessage(game, null, playerInventory.replace(/, $/, '').trim());
}
const getActiveCount = (game) => {
let active = 0;
for (let color in game.players) {
@ -2182,11 +2292,15 @@ const sendGame = async (req, res, game, error) => {
}
player.points += MAX_SETTLEMENTS - player.settlements;
player.points += 2 * (MAX_CITIES - player.cities);
player.unplayed = 0;
player.development.forEach(card => {
if (card.type === 'vp' && card.played) {
player.points++;
}
if (!card.played) {
player.unplayed++;
}
});
if (!game.winner && (player.points >= 10 && session.color === key)) {
@ -2195,6 +2309,8 @@ const sendGame = async (req, res, game, error) => {
game.state = 'winner';
delete game.turn.roll;
}
}
/* Shallow copy game, filling its sessions with a shallow copy of sessions so we can then
@ -2213,6 +2329,12 @@ const sendGame = async (req, res, game, error) => {
reducedSessions.push(reduced);
}
/* Save per turn while debugging... */
await writeFile(`games/${game.id}.${game.turns}`, JSON.stringify(reducedGame, null, 2))
.catch((error) => {
console.error(`Unable to write to games/${game.id}`);
console.error(error);
});
await writeFile(`games/${game.id}`, JSON.stringify(reducedGame, null, 2))
.catch((error) => {
console.error(`Unable to write to games/${game.id}`);