const { layout } = require('../util/layout.js'); 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 0; } /* If this corner is already being walked, skip it */ if (placedCorner.walking) { return 0; } 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]; 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; }; 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 0; } /* If this road is already being walked, skip it */ if (placedRoad.walking) { return 0; } placedRoad.walking = true; /* Calculate the longest road branching from both corners */ let roadLength = 1; layout.roads[roadIndex].corners.forEach(cornerIndex => { const placedCorner = game.placements.corners[cornerIndex]; if (placedCorner.walking) { return; } roadLength += processCorner(game, color, cornerIndex, placedCorner); }); return roadLength; }; 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 clearRoadWalking = (game) => { /* Clear out walk markers on roads */ layout.roads.forEach((item, itemIndex) => { delete game.placements.roads[itemIndex].walking; }); /* Clear out walk markers on corners */ layout.corners.forEach((item, itemIndex) => { delete game.placements.corners[itemIndex].walking; }); } const calculateRoadLengths = (game) => { const color = game.color; clearRoadWalking(game); /* 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((_, roadIndex) => { const placedRoad = game.placements.roads[roadIndex]; if (placedRoad.color === color) { let set = []; buildRoadGraph(game, color, roadIndex, placedRoad, set); if (set.length) { graphs.push({ color, set }); } } }); let final = { segments: 0, index: -1 }; clearRoadWalking(game); graphs.forEach(graph => { graph.longestRoad = 0; graph.set.forEach(roadIndex => { const placedRoad = game.placements.roads[roadIndex]; clearRoadWalking(game); const length = processRoad(game, color, roadIndex, placedRoad); if (length >= graph.longestRoad) { graph.longestStartSegment = roadIndex; graph.longestRoad = length; if (length > final.segments) { final.segments = length; final.index = roadIndex; } } }); }); game.placements.roads.forEach(road => delete road.walking); return final; }; module.exports = calculateRoadLengths;