Skip to content

Commit

Permalink
Generate launches button
Browse files Browse the repository at this point in the history
  • Loading branch information
Grzegorz Tańczyk committed Jun 30, 2024
1 parent 11c8e52 commit 8118a98
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 3 deletions.
37 changes: 35 additions & 2 deletions games/nukes/src/controls-render/infotainment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ import styled from 'styled-components';

import { City, State, WorldState } from '../world/world-state-types';
import { getValueInTime } from '../world/world-state-time-utils';
import { generateLaunches } from '../world/generate-launches';
import { usePointer } from '../controls/pointer';

export function Infotainment({ worldState }: { worldState: WorldState }) {
export function Infotainment({
worldState,
setWorldState,
}: {
worldState: WorldState;
setWorldState: (worldState: WorldState) => void;
}) {
const pointer = usePointer();

const cityPopulation: Array<[City, number]> = worldState.cities.map((city) => [
Expand Down Expand Up @@ -63,6 +70,9 @@ export function Infotainment({ worldState }: { worldState: WorldState }) {
}
/>
</li>
<li>
<GenerateLaunches worldState={worldState} setWorldState={setWorldState} />
</li>
</ul>
</InfotainmentContainer>
);
Expand All @@ -73,6 +83,30 @@ function CopyToClipboard({ getText }: { getText: () => string }) {
return <button onClick={() => navigator.clipboard.writeText(getText())}>Copy world state</button>;
}

// a component that generates launches
function GenerateLaunches({
worldState,
setWorldState,
}: {
worldState: WorldState;
setWorldState: (worldState: WorldState) => void;
}) {
return (
<button
onClick={() => {
const { missiles, explosions } = generateLaunches(worldState);
setWorldState({
...worldState,
missiles: [...worldState.missiles, ...missiles],
explosions: [...worldState.explosions, ...explosions],
});
}}
>
Generate launches
</button>
);
}

const InfotainmentContainer = styled.div`
position: fixed;
right: 0;
Expand All @@ -81,7 +115,6 @@ const InfotainmentContainer = styled.div`
max-width: 25%;
min-width: 200px;
max-height: 25%;
min-height: 200px;
overflow-y: auto;
Expand Down
2 changes: 1 addition & 1 deletion games/nukes/src/game-states/state-tech-world.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const WorldComponent: GameStateComponent = ({}) => {
<WorldStateRender state={worldState} />

<LaunchHighlight />
<Infotainment worldState={worldState} />
<Infotainment worldState={worldState} setWorldState={setWorldState} />
</StateContainer>
</PointerContextWrapper>
</SelectionContextWrapper>
Expand Down
155 changes: 155 additions & 0 deletions games/nukes/src/world/generate-launches.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// prompt: 2940295303300907008

import { WorldState, Missile, Explosion } from './world-state-types';
import { distance } from '../math/position-utils';
import {
EXPLOSION_DAMAGE_RATIO,
EXPLOSION_DURATION,
EXPLOSION_RADIUS,
MIN_EXPLOSION_DAMAGE,
MISSILE_SPEED,
} from './world-state-constants';

/**
* For each state, determine launch targets and generate missile and explosion events.
*
* This function aims to strategically target cities to maximize damage to enemy states,
* taking into account existing missiles and future explosions to optimize target distribution.
*
* @param state Current world state
* @returns A tuple containing two arrays: new missiles and their corresponding explosions
*/
export function generateLaunches(state: WorldState) {
const newMissiles: Missile[] = [];
const newExplosions: Explosion[] = [];

// Iterate through states to plan launches for each state's launch sites
for (const currentState of state.states) {
// Get launch sites belonging to the current state
const stateLaunchSites = state.launchSites.filter((launchSite) => launchSite.stateId === currentState.id);

// For each launch site, determine and execute a launch
for (const launchSite of stateLaunchSites) {
// Find potential target cities in other states
const potentialTargets = state.cities.filter((city) => city.stateId !== currentState.id);

// Rank potential targets based on strategic value
const rankedTargets = rankTargets(state, launchSite.position, potentialTargets);

// If there are any valid targets
if (rankedTargets.length > 0) {
const targetCity = rankedTargets[0]; // Choose the highest-ranked target

// Calculate missile flight time
const distanceToTarget = distance(
launchSite.position.x,
launchSite.position.y,
targetCity.position.x,
targetCity.position.y,
);
const missileFlightTime = distanceToTarget / MISSILE_SPEED;

// Create new missile
const newMissile: Missile = {
id: `missile-${Math.random().toString(36).substring(2, 15)}`, // Generate a unique ID
launch: launchSite.position,
launchTimestamp: state.timestamp,
target: targetCity.position,
targetTimestamp: state.timestamp + missileFlightTime,
};
newMissiles.push(newMissile);

// Create corresponding explosion
const newExplosion: Explosion = {
id: `explosion-${Math.random().toString(36).substring(2, 15)}`, // Generate a unique ID
startTimestamp: state.timestamp + missileFlightTime,
endTimestamp: state.timestamp + missileFlightTime + EXPLOSION_DURATION,
position: targetCity.position,
radius: EXPLOSION_RADIUS,
};
newExplosions.push(newExplosion);
}
}
}

return { explosions: newExplosions, missiles: newMissiles };
}

/**
* Rank potential target cities based on strategic value.
*
* This function considers factors like remaining population, proximity to other targets,
* and existing missile/explosion activity to prioritize targets that maximize damage.
*
* @param state Current world state
* @param launchPosition Position of the launching entity
* @param potentialTargets Array of potential target cities
* @returns Sorted array of target cities in descending order of strategic value
*/
function rankTargets(
state: WorldState,
_launchPosition: { x: number; y: number },
potentialTargets: {
id: string;
stateId: string;
name: string;
position: { x: number; y: number };
populationHistogram: { timestamp: number; population: number }[];
}[],
): {
id: string;
stateId: string;
name: string;
position: { x: number; y: number };
populationHistogram: { timestamp: number; population: number }[];
}[] {
return potentialTargets
.map((target) => {
// Calculate remaining population after considering future explosions
const futurePopulation = calculateFuturePopulation(state, target);

// Prioritize targets with higher remaining population
return {
...target,
futurePopulation,
value: futurePopulation * target.populationHistogram.length,
};
})
.sort((a, b) => b.value - a.value); // Sort by value in descending order
}

/**
* Calculate the future population of a city after accounting for upcoming explosions.
*
* This function iterates through future explosions and estimates the population reduction
* based on the explosion's damage radius and the city's distance from the epicenter.
*
* @param state Current world state
* @param city Target city for population calculation
* @returns Estimated future population of the city
*/
function calculateFuturePopulation(
state: WorldState,
city: {
id: string;
stateId: string;
name: string;
position: { x: number; y: number };
populationHistogram: { timestamp: number; population: number }[];
},
): number {
let futurePopulation = city.populationHistogram[city.populationHistogram.length - 1].population;

// Consider future explosions
for (const explosion of state.explosions.filter((e) => e.startTimestamp >= state.timestamp)) {
const distanceToExplosion = distance(city.position.x, city.position.y, explosion.position.x, explosion.position.y);

// If within explosion radius, calculate population reduction
if (distanceToExplosion <= explosion.radius) {
const damage = Math.max(MIN_EXPLOSION_DAMAGE, futurePopulation * EXPLOSION_DAMAGE_RATIO);
futurePopulation -= damage;
}
}

return Math.max(0, futurePopulation); // Ensure population doesn't go negative
}
6 changes: 6 additions & 0 deletions games/nukes/src/world/world-state-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ export const EXPLOSION_SPEED = 5;

/** How long does the explosion take */
export const EXPLOSION_DURATION = EXPLOSION_RADIUS / EXPLOSION_SPEED;

/** Explosion damage ratio */
export const EXPLOSION_DAMAGE_RATIO = 0.5;

/** Minimum explosion damage */
export const MIN_EXPLOSION_DAMAGE = 10;

0 comments on commit 8118a98

Please sign in to comment.