Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -1,17 +1,32 @@
|
||||
import { GameField, BoardData } from '../../Domain/Game/GameAggregate';
|
||||
import { logOther, logError } from '../Services/Logger';
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
interface TargetField {
|
||||
fieldNumber: number;
|
||||
distance: number;
|
||||
}
|
||||
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
interface SpecialFieldInfo {
|
||||
position: number;
|
||||
type: 'positive' | 'negative' | 'luck';
|
||||
}
|
||||
|
||||
export class BoardGenerationService {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
private readonly MAX_GENERATION_TIME = parseInt(process.env.MAX_GENERATION_TIME_SECONDS || '20') * 1000;
|
||||
private readonly ERROR_TOLERANCE = parseInt(process.env.GENERATION_ERROR_TOLERANCE || '15');
|
||||
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
async generateBoard(
|
||||
positiveFieldCount: number,
|
||||
negativeFieldCount: number,
|
||||
luckFieldCount: number
|
||||
): Promise<BoardData> {
|
||||
<<<<<<< HEAD
|
||||
// Pattern-based approach has 100% success rate, no retry needed
|
||||
const result = this.generateSingleAttempt(positiveFieldCount, negativeFieldCount, luckFieldCount);
|
||||
|
||||
@@ -24,6 +39,36 @@ export class BoardGenerationService {
|
||||
});
|
||||
|
||||
return result;
|
||||
=======
|
||||
const startTime = Date.now();
|
||||
let bestAttempt: BoardData | null = null;
|
||||
let attemptCount = 0;
|
||||
|
||||
while (Date.now() - startTime < this.MAX_GENERATION_TIME) {
|
||||
attemptCount++;
|
||||
|
||||
try {
|
||||
const attempt = this.generateSingleAttempt(positiveFieldCount, negativeFieldCount, luckFieldCount);
|
||||
|
||||
if (attempt.totalErrorRate <= this.ERROR_TOLERANCE) {
|
||||
logOther(`Board generation successful on attempt ${attemptCount}. Error rate: ${attempt.totalErrorRate}%`);
|
||||
return attempt;
|
||||
}
|
||||
|
||||
if (!bestAttempt || attempt.totalErrorRate < bestAttempt.totalErrorRate) {
|
||||
bestAttempt = attempt;
|
||||
}
|
||||
|
||||
logOther(`Attempt ${attemptCount}: Error rate ${attempt.totalErrorRate}% (target: ${this.ERROR_TOLERANCE}%)`);
|
||||
|
||||
} catch (error) {
|
||||
logError(`Board generation attempt ${attemptCount} failed:`, error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
logOther(`Using best attempt with error rate: ${bestAttempt?.totalErrorRate || 100}%`);
|
||||
return bestAttempt || this.generateFallbackBoard(positiveFieldCount, negativeFieldCount, luckFieldCount);
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
}
|
||||
|
||||
private generateSingleAttempt(
|
||||
@@ -38,11 +83,42 @@ export class BoardGenerationService {
|
||||
luckFieldCount
|
||||
);
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Step 2: Calculate step values using pattern-based approach
|
||||
const fields = this.calculatePatternBasedStepValues(specialFieldPositions);
|
||||
|
||||
return {
|
||||
fields
|
||||
=======
|
||||
// Step 2: Select target fields for each special field (6 targets per field for dice 1-6)
|
||||
const targetFieldsMap = this.selectTargetFields(specialFieldPositions);
|
||||
|
||||
// Step 3: Create border with strategic placement
|
||||
const border = this.createStrategicBorder(targetFieldsMap);
|
||||
|
||||
// Step 4: Calculate step values based on border positions
|
||||
const fields = this.calculateStepValues(specialFieldPositions, targetFieldsMap, border);
|
||||
|
||||
// Step 5: Validate against 20-30 rule and calculate error rate
|
||||
const validationResults = this.validateBoardGeneration(fields, border);
|
||||
|
||||
// Log generation statistics
|
||||
logOther('Board generation attempt completed', {
|
||||
totalFields: fields.length,
|
||||
specialFields: fields.filter(f => f.type !== 'regular').length,
|
||||
positiveFields: fields.filter(f => f.type === 'positive').length,
|
||||
negativeFields: fields.filter(f => f.type === 'negative').length,
|
||||
luckFields: fields.filter(f => f.type === 'luck').length,
|
||||
errorRate: validationResults.errorRate,
|
||||
targetCount: Array.from(targetFieldsMap.values()).reduce((sum, targets) => sum + targets.length, 0)
|
||||
});
|
||||
|
||||
return {
|
||||
fields,
|
||||
border,
|
||||
validationResults: validationResults.validationResults,
|
||||
totalErrorRate: validationResults.errorRate
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,6 +128,7 @@ export class BoardGenerationService {
|
||||
luckFieldCount: number
|
||||
): SpecialFieldInfo[] {
|
||||
const totalSpecial = positiveFieldCount + negativeFieldCount + luckFieldCount;
|
||||
<<<<<<< HEAD
|
||||
const specialFields: SpecialFieldInfo[] = [];
|
||||
|
||||
// Generate unique random positions
|
||||
@@ -63,6 +140,29 @@ export class BoardGenerationService {
|
||||
|
||||
// Convert to sorted array
|
||||
const sortedPositions = Array.from(positions).sort((a, b) => a - b);
|
||||
=======
|
||||
const positions: number[] = [];
|
||||
const specialFields: SpecialFieldInfo[] = [];
|
||||
|
||||
// Random placement with retry for good distribution
|
||||
let attempts = 0;
|
||||
while (positions.length < totalSpecial && attempts < 100) {
|
||||
const position = Math.floor(Math.random() * 100) + 1; // 1-100
|
||||
|
||||
if (!positions.includes(position)) {
|
||||
// Check minimum distance from existing positions
|
||||
const tooClose = positions.some(existingPos => Math.abs(existingPos - position) < 3);
|
||||
|
||||
if (!tooClose || attempts > 50) { // Relax distance requirement after many attempts
|
||||
positions.push(position);
|
||||
}
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
|
||||
// Sort positions and assign types
|
||||
positions.sort((a, b) => a - b);
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
|
||||
// Distribute types randomly
|
||||
const types: ('positive' | 'negative' | 'luck')[] = [
|
||||
@@ -77,7 +177,11 @@ export class BoardGenerationService {
|
||||
[types[i], types[j]] = [types[j], types[i]];
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
sortedPositions.forEach((position, index) => {
|
||||
=======
|
||||
positions.forEach((position, index) => {
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
specialFields.push({
|
||||
position,
|
||||
type: types[index] || 'positive'
|
||||
@@ -87,14 +191,156 @@ export class BoardGenerationService {
|
||||
return specialFields;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
private calculatePatternBasedStepValues(specialFields: SpecialFieldInfo[]): GameField[] {
|
||||
=======
|
||||
private selectTargetFields(specialFields: SpecialFieldInfo[]): Map<number, TargetField[]> {
|
||||
const targetFieldsMap = new Map<number, TargetField[]>();
|
||||
|
||||
specialFields.forEach(field => {
|
||||
if (field.type === 'luck') {
|
||||
// Luck fields don't need target calculations
|
||||
targetFieldsMap.set(field.position, []);
|
||||
return;
|
||||
}
|
||||
|
||||
const targets: TargetField[] = [];
|
||||
const usedTargets = new Set<number>();
|
||||
|
||||
// Generate 6 different target fields (for dice 1-6) with 20-30 rule compliance
|
||||
for (let i = 0; i < 6; i++) {
|
||||
let targetField: number;
|
||||
let distance: number;
|
||||
let attempts = 0;
|
||||
|
||||
do {
|
||||
// Determine max distance based on field position (20-30 rule)
|
||||
let maxDistance: number;
|
||||
let maxBackward: number;
|
||||
|
||||
if (field.position <= 85) {
|
||||
maxDistance = 20;
|
||||
maxBackward = 20;
|
||||
} else {
|
||||
maxDistance = 20; // forward
|
||||
maxBackward = 30; // backward
|
||||
}
|
||||
|
||||
// Create variety in distances within the allowed range
|
||||
const distanceType = Math.random();
|
||||
if (distanceType < 0.5) {
|
||||
// Close distance (50% chance) - 1 to 1/3 of max
|
||||
distance = Math.floor(Math.random() * Math.floor(maxDistance / 3)) + 1;
|
||||
} else {
|
||||
// Far distance (50% chance) - 1/3 to max
|
||||
distance = Math.floor(Math.random() * (maxDistance - Math.floor(maxDistance / 3))) + Math.floor(maxDistance / 3);
|
||||
}
|
||||
|
||||
// Randomly choose forward or backward
|
||||
if (Math.random() < 0.5) {
|
||||
distance = -Math.min(distance, maxBackward);
|
||||
} else {
|
||||
distance = Math.min(distance, maxDistance);
|
||||
}
|
||||
|
||||
targetField = field.position + distance;
|
||||
|
||||
// Ensure target is within valid range
|
||||
if (targetField < 1) targetField = 1;
|
||||
if (targetField > 100) targetField = 100;
|
||||
|
||||
// Recalculate actual distance after clamping
|
||||
distance = Math.abs(targetField - field.position);
|
||||
|
||||
attempts++;
|
||||
} while (usedTargets.has(targetField) && attempts < 30);
|
||||
|
||||
if (!usedTargets.has(targetField)) {
|
||||
usedTargets.add(targetField);
|
||||
targets.push({
|
||||
fieldNumber: targetField,
|
||||
distance: Math.abs(targetField - field.position)
|
||||
});
|
||||
} else {
|
||||
// Fallback: use a nearby valid target
|
||||
let fallbackTarget = field.position + (i - 3); // Create some variety around current position
|
||||
if (fallbackTarget < 1) fallbackTarget = 1;
|
||||
if (fallbackTarget > 100) fallbackTarget = 100;
|
||||
|
||||
targets.push({
|
||||
fieldNumber: fallbackTarget,
|
||||
distance: Math.abs(fallbackTarget - field.position)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
targetFieldsMap.set(field.position, targets);
|
||||
});
|
||||
|
||||
return targetFieldsMap;
|
||||
}
|
||||
|
||||
private createStrategicBorder(targetFieldsMap: Map<number, TargetField[]>): number[] {
|
||||
// Collect all target field numbers
|
||||
const targetNumbers = new Set<number>();
|
||||
targetFieldsMap.forEach(targets => {
|
||||
targets.forEach(target => targetNumbers.add(target.fieldNumber));
|
||||
});
|
||||
|
||||
// Create array of all numbers 1-100
|
||||
const allNumbers = Array.from({ length: 100 }, (_, i) => i + 1);
|
||||
|
||||
// Separate target numbers from remaining numbers
|
||||
const remainingNumbers = allNumbers.filter(num => !targetNumbers.has(num));
|
||||
|
||||
// Shuffle remaining numbers
|
||||
for (let i = remainingNumbers.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[remainingNumbers[i], remainingNumbers[j]] = [remainingNumbers[j], remainingNumbers[i]];
|
||||
}
|
||||
|
||||
// Create border with strategic placement
|
||||
const border: number[] = [];
|
||||
const targetArray = Array.from(targetNumbers);
|
||||
|
||||
// Encourage overlap by placing target numbers first, then fill with random
|
||||
let targetIndex = 0;
|
||||
let remainingIndex = 0;
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
// Alternate between target numbers and remaining numbers, but favor targets when available
|
||||
if (targetIndex < targetArray.length && (remainingIndex >= remainingNumbers.length || Math.random() < 0.6)) {
|
||||
border.push(targetArray[targetIndex]);
|
||||
targetIndex++;
|
||||
} else if (remainingIndex < remainingNumbers.length) {
|
||||
border.push(remainingNumbers[remainingIndex]);
|
||||
remainingIndex++;
|
||||
} else {
|
||||
// Fallback - should not happen if logic is correct
|
||||
border.push((i % 100) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return border;
|
||||
}
|
||||
|
||||
private calculateStepValues(
|
||||
specialFields: SpecialFieldInfo[],
|
||||
targetFieldsMap: Map<number, TargetField[]>,
|
||||
border: number[]
|
||||
): GameField[] {
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
// Initialize all fields as regular
|
||||
const fields: GameField[] = Array.from({ length: 100 }, (_, i) => ({
|
||||
position: i + 1,
|
||||
type: 'regular' as const
|
||||
}));
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Update special fields with pattern-based step values
|
||||
=======
|
||||
// Update special fields with calculated step values
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
specialFields.forEach(specialField => {
|
||||
const fieldIndex = specialField.position - 1; // Convert to 0-based index
|
||||
fields[fieldIndex].type = specialField.type;
|
||||
@@ -104,6 +350,7 @@ export class BoardGenerationService {
|
||||
return;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Calculate step values based on position rules
|
||||
let maxStepValue: number;
|
||||
let minStepValue: number;
|
||||
@@ -127,12 +374,64 @@ export class BoardGenerationService {
|
||||
// Negative fields: use negative step values (-3 to -8 range)
|
||||
const stepValue = -(Math.floor(Math.random() * 6) + 3); // -3 to -8
|
||||
fields[fieldIndex].stepValue = Math.max(stepValue, minStepValue);
|
||||
=======
|
||||
const targets = targetFieldsMap.get(specialField.position) || [];
|
||||
if (targets.length === 0) return;
|
||||
|
||||
// NEW APPROACH: Calculate step value that will land on first target with dice=1
|
||||
// This ensures we have a baseline that works, then dice 2-6 will hit other targets
|
||||
const firstTarget = targets[0];
|
||||
const targetIndexInBorder = border.indexOf(firstTarget.fieldNumber);
|
||||
|
||||
if (targetIndexInBorder !== -1) {
|
||||
// Start from field position in border (field position = border index + 1, but we want 0-based)
|
||||
const startBorderIndex = (specialField.position - 1) % border.length;
|
||||
|
||||
// Calculate step value needed to reach target with dice=1
|
||||
let stepValue: number;
|
||||
|
||||
if (specialField.type === 'positive') {
|
||||
// For positive: move right to target, then +1 more for dice=1
|
||||
stepValue = targetIndexInBorder - startBorderIndex - 1; // -1 for dice offset
|
||||
|
||||
// Handle wrap-around
|
||||
if (stepValue < 0) {
|
||||
stepValue += border.length;
|
||||
}
|
||||
} else {
|
||||
// For negative: move left to target, then -1 more for dice=1
|
||||
stepValue = startBorderIndex - targetIndexInBorder + 1; // +1 for dice offset
|
||||
|
||||
// Handle wrap-around
|
||||
if (stepValue > border.length) {
|
||||
stepValue -= border.length;
|
||||
}
|
||||
|
||||
// Make negative for negative fields
|
||||
stepValue = -stepValue;
|
||||
}
|
||||
|
||||
fields[fieldIndex].stepValue = stepValue;
|
||||
|
||||
// Debug logging for step value calculation
|
||||
logOther(`Calculated step value for ${specialField.type} field at position ${specialField.position}`, {
|
||||
targetField: firstTarget.fieldNumber,
|
||||
targetIndexInBorder,
|
||||
startBorderIndex,
|
||||
calculatedStepValue: stepValue,
|
||||
fieldType: specialField.type
|
||||
});
|
||||
} else {
|
||||
// Fallback if target not found in border (shouldn't happen)
|
||||
fields[fieldIndex].stepValue = specialField.type === 'positive' ? 1 : -1;
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
}
|
||||
});
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// This method can be used by FieldEffectService for movement calculations
|
||||
public calculatePatternBasedMovement(
|
||||
currentPosition: number,
|
||||
@@ -175,6 +474,89 @@ export class BoardGenerationService {
|
||||
} else {
|
||||
return 0; // Other even positions
|
||||
}
|
||||
=======
|
||||
private validateBoardGeneration(fields: GameField[], border: number[]): {
|
||||
validationResults: { [fieldIndex: number]: number[] };
|
||||
errorRate: number;
|
||||
} {
|
||||
const validationResults: { [fieldIndex: number]: number[] } = {};
|
||||
let totalCombinations = 0;
|
||||
let invalidCombinations = 0;
|
||||
|
||||
fields.forEach((field, fieldIndex) => {
|
||||
if (field.type !== 'positive' && field.type !== 'negative') {
|
||||
return; // Skip non-special fields
|
||||
}
|
||||
|
||||
const diceOutcomes: number[] = [];
|
||||
|
||||
for (let diceValue = 1; diceValue <= 6; diceValue++) {
|
||||
totalCombinations++;
|
||||
|
||||
try {
|
||||
const result = this.calculateBorderMovement(
|
||||
field.position,
|
||||
field.stepValue || 0,
|
||||
diceValue,
|
||||
border,
|
||||
field.type === 'positive'
|
||||
);
|
||||
|
||||
// Validate 20-30 rule
|
||||
const distance = Math.abs(result - field.position);
|
||||
const isValid = this.validate20_30Rule(field.position, result, distance);
|
||||
|
||||
if (isValid) {
|
||||
diceOutcomes.push(result);
|
||||
} else {
|
||||
diceOutcomes.push(-1); // Mark as invalid
|
||||
invalidCombinations++;
|
||||
}
|
||||
} catch (error) {
|
||||
diceOutcomes.push(-1); // Mark as invalid
|
||||
invalidCombinations++;
|
||||
}
|
||||
}
|
||||
|
||||
validationResults[fieldIndex] = diceOutcomes;
|
||||
});
|
||||
|
||||
const errorRate = totalCombinations > 0 ? (invalidCombinations / totalCombinations) * 100 : 0;
|
||||
|
||||
return {
|
||||
validationResults,
|
||||
errorRate: Math.round(errorRate * 100) / 100 // Round to 2 decimal places
|
||||
};
|
||||
}
|
||||
|
||||
private calculateBorderMovement(
|
||||
currentPosition: number,
|
||||
stepValue: number,
|
||||
diceValue: number,
|
||||
border: number[],
|
||||
isPositive: boolean
|
||||
): number {
|
||||
// Step 1: Find border index for current field (field position corresponds to border index)
|
||||
let borderIndex = (currentPosition - 1) % border.length; // Convert to 0-based, handle wraparound
|
||||
|
||||
// Step 2: Apply field step value (handle negative step values for negative fields)
|
||||
if (isPositive) {
|
||||
borderIndex = (borderIndex + Math.abs(stepValue)) % border.length;
|
||||
} else {
|
||||
// For negative fields, stepValue is already negative, so we subtract it (which adds its absolute value)
|
||||
borderIndex = (borderIndex - stepValue + border.length) % border.length;
|
||||
}
|
||||
|
||||
// Step 3: Apply dice value
|
||||
if (isPositive) {
|
||||
borderIndex = (borderIndex + diceValue) % border.length;
|
||||
} else {
|
||||
borderIndex = (borderIndex - diceValue + border.length) % border.length;
|
||||
}
|
||||
|
||||
// Step 4: Return the field number at final border position
|
||||
return border[borderIndex];
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
}
|
||||
|
||||
private validate20_30Rule(currentPosition: number, targetPosition: number, distance: number): boolean {
|
||||
@@ -196,4 +578,46 @@ export class BoardGenerationService {
|
||||
|
||||
return false;
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
private generateFallbackBoard(
|
||||
positiveFieldCount: number,
|
||||
negativeFieldCount: number,
|
||||
luckFieldCount: number
|
||||
): BoardData {
|
||||
// Simple fallback: create basic board with minimal special fields
|
||||
const fields: GameField[] = Array.from({ length: 100 }, (_, i) => ({
|
||||
position: i + 1,
|
||||
type: 'regular' as const
|
||||
}));
|
||||
|
||||
// Add a few special fields with safe step values
|
||||
let specialCount = 0;
|
||||
for (let i = 10; i < 90 && specialCount < positiveFieldCount + negativeFieldCount; i += 10) {
|
||||
if (specialCount < positiveFieldCount) {
|
||||
fields[i].type = 'positive';
|
||||
fields[i].stepValue = 1;
|
||||
} else {
|
||||
fields[i].type = 'negative';
|
||||
fields[i].stepValue = -1;
|
||||
}
|
||||
specialCount++;
|
||||
}
|
||||
|
||||
// Simple border: shuffled 1-100
|
||||
const border = Array.from({ length: 100 }, (_, i) => i + 1);
|
||||
for (let i = border.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[border[i], border[j]] = [border[j], border[i]];
|
||||
}
|
||||
|
||||
return {
|
||||
fields,
|
||||
border,
|
||||
validationResults: {},
|
||||
totalErrorRate: 100 // Mark as fallback
|
||||
};
|
||||
}
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
}
|
||||
@@ -37,7 +37,11 @@ export class GenerateBoardCommandHandler {
|
||||
);
|
||||
|
||||
const executionTime = Date.now() - startTime;
|
||||
<<<<<<< HEAD
|
||||
logOther(`Board generation completed for game ${cmd.gameId} in ${executionTime}ms using pattern-based approach`);
|
||||
=======
|
||||
logOther(`Board generation completed for game ${cmd.gameId} in ${executionTime}ms. Error rate: ${boardData.totalErrorRate}%`);
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
|
||||
} catch (error) {
|
||||
logError(`Board generation failed for game ${cmd.gameId}:`, error as Error);
|
||||
@@ -46,6 +50,12 @@ export class GenerateBoardCommandHandler {
|
||||
const errorData: BoardData = {
|
||||
gameId: cmd.gameId,
|
||||
fields: [],
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
border: [],
|
||||
validationResults: {},
|
||||
totalErrorRate: 100,
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
generationComplete: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
generatedAt: new Date()
|
||||
|
||||
@@ -151,6 +151,7 @@ export class JoinGameCommandHandler {
|
||||
isOnline: true
|
||||
};
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Check if player name is already in use by a different player
|
||||
const existingPlayerWithName = gameData.currentPlayers.find(
|
||||
p => p.playerName === command.playerName && p.playerId !== command.playerId
|
||||
@@ -160,6 +161,8 @@ export class JoinGameCommandHandler {
|
||||
throw new Error(`Player name "${command.playerName}" is already in use in this game`);
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
// Update players list (remove if exists, then add)
|
||||
gameData.currentPlayers = gameData.currentPlayers.filter(p => p.playerId !== command.playerId);
|
||||
gameData.currentPlayers.push(newPlayer);
|
||||
@@ -170,6 +173,12 @@ export class JoinGameCommandHandler {
|
||||
// Store updated data in Redis with TTL (24 hours)
|
||||
await this.redisService.setWithExpiry(redisKey, JSON.stringify(gameData), 24 * 60 * 60);
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
// Add player to active players set
|
||||
await this.redisService.setAdd(`active_players:${game.id}`, command.playerId);
|
||||
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
logOther('Game data updated in Redis', {
|
||||
gameId: game.id,
|
||||
gameCode: game.gamecode,
|
||||
@@ -210,6 +219,10 @@ export class JoinGameCommandHandler {
|
||||
gameData.currentPlayers = gameData.currentPlayers.filter(p => p.playerId !== playerId);
|
||||
|
||||
await this.redisService.setWithExpiry(redisKey, JSON.stringify(gameData), 24 * 60 * 60);
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
await this.redisService.setRemove(`active_players:${gameId}`, playerId);
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
}
|
||||
} catch (error) {
|
||||
logError('Failed to remove player from Redis', error instanceof Error ? error : new Error(String(error)));
|
||||
|
||||
@@ -64,7 +64,11 @@ export class StartGameCommandHandler {
|
||||
gamecode,
|
||||
maxplayers: command.maxplayers,
|
||||
logintype: command.logintype,
|
||||
<<<<<<< HEAD
|
||||
createdby: command.userid!,
|
||||
=======
|
||||
createdby: command.userid || null,
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
orgid: command.orgid || null,
|
||||
gamedecks,
|
||||
players: [],
|
||||
|
||||
@@ -28,7 +28,11 @@ export interface ActiveGamePlayData {
|
||||
turnSequence: string[]; // Ordered array of player IDs based on turnOrder
|
||||
websocketRoom: string;
|
||||
gamePhase: 'starting' | 'playing' | 'paused' | 'finished';
|
||||
<<<<<<< HEAD
|
||||
boardData: BoardData; // Generated board with fields
|
||||
=======
|
||||
boardData: BoardData; // Generated board with fields and border
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
}
|
||||
|
||||
export interface GameStartResult {
|
||||
@@ -362,7 +366,13 @@ export class StartGamePlayCommandHandler {
|
||||
logOther(`Board data found for game ${gameId}`, {
|
||||
generationComplete: boardData.generationComplete,
|
||||
hasError: !!boardData.error,
|
||||
<<<<<<< HEAD
|
||||
fieldsCount: boardData.fields ? boardData.fields.length : 0
|
||||
=======
|
||||
fieldsCount: boardData.fields ? boardData.fields.length : 0,
|
||||
borderLength: boardData.border ? boardData.border.length : 0,
|
||||
totalErrorRate: boardData.totalErrorRate
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
});
|
||||
|
||||
if (boardData.generationComplete) {
|
||||
@@ -372,7 +382,13 @@ export class StartGamePlayCommandHandler {
|
||||
}
|
||||
|
||||
logOther(`Board generation completed for game ${gameId}`, {
|
||||
<<<<<<< HEAD
|
||||
fieldCount: boardData.fields.length,
|
||||
=======
|
||||
errorRate: boardData.totalErrorRate,
|
||||
fieldCount: boardData.fields.length,
|
||||
borderLength: boardData.border.length,
|
||||
>>>>>>> 83fad59878db015ec8d86bdec1ecbbca0baddfd2
|
||||
waitTime: Date.now() - startTime
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user