Skip to content

Instantly share code, notes, and snippets.

@mossyblog
Created March 6, 2025 05:16
Show Gist options
  • Save mossyblog/aad9492e0d4aab0b2d8d89d02a1e86f0 to your computer and use it in GitHub Desktop.
Save mossyblog/aad9492e0d4aab0b2d8d89d02a1e86f0 to your computer and use it in GitHub Desktop.
import React, { useState, useEffect } from 'react';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, LineChart, Line } from 'recharts';
const TariffWarSimulation = () => {
const [usTariffRate, setUsTariffRate] = useState(25);
const [retaliationLevel, setRetaliationLevel] = useState(100);
const [australiaPosition, setAustraliaPosition] = useState('us-aligned');
const [chinaRefinementBlock, setChinaRefinementBlock] = useState(0);
const [ukraineMineralsToEU, setUkraineMineralsToEU] = useState(0);
const [russiaControlsUSMinerals, setRussiaControlsUSMinerals] = useState(0);
const [hasRun, setHasRun] = useState(false);
const [results, setResults] = useState(null);
// Define initial country data
const initialCountryData = {
us: {
name: 'United States',
gdp: 23000, // billions USD
gdpGrowth: 2.0,
inflation: 2.0,
employment: 3.5, // unemployment rate
tradeBalance: -800, // billions USD
consumerPrices: 0,
manufacturing: 0,
exports: 2500,
imports: 3300,
rareMineralAccess: 100, // percentage of normal access
refinedMaterialsAccess: 100,
techManufacturingCapacity: 100
},
china: {
name: 'China',
gdp: 17000,
gdpGrowth: 5.5,
inflation: 2.0,
employment: 5.0,
tradeBalance: 600,
consumerPrices: 0,
manufacturing: 0,
exports: 3100,
imports: 2500,
mineralRefinementCapacity: 100, // percentage of global capacity
rareMineralReserves: 100, // percentage of normal reserves
techManufacturingCapacity: 100
},
eu: {
name: 'European Union',
gdp: 15000,
gdpGrowth: 1.5,
inflation: 2.0,
employment: 6.5,
tradeBalance: 250,
consumerPrices: 0,
manufacturing: 0,
exports: 2800,
imports: 2550,
ukrainianMineralAccess: 100, // percentage access to Ukrainian resources
refinedMaterialsAccess: 100,
techManufacturingCapacity: 100
},
japan: {
name: 'Japan',
gdp: 5000,
gdpGrowth: 1.0,
inflation: 1.0,
employment: 2.5,
tradeBalance: 50,
consumerPrices: 0,
manufacturing: 0,
exports: 800,
imports: 750,
refinedMaterialsAccess: 100,
techManufacturingCapacity: 100
},
australia: {
name: 'Australia',
gdp: 1400,
gdpGrowth: 2.5,
inflation: 2.0,
employment: 5.0,
tradeBalance: -20,
consumerPrices: 0,
manufacturing: 0,
resourceExports: 300,
exports: 450,
imports: 470,
rareMineralReserves: 100
},
russia: {
name: 'Russia',
gdp: 1800,
gdpGrowth: 1.0,
inflation: 4.0,
employment: 4.5,
tradeBalance: 120,
consumerPrices: 0,
manufacturing: 0,
exports: 550,
imports: 430,
resourceControl: 100, // control over US-accessible minerals
rareMineralReserves: 100
}
};
const [countryData, setCountryData] = useState(initialCountryData);
const [globalData, setGlobalData] = useState({
gdpImpact: 0,
tradeVolume: 0
});
// Reset simulation
const resetSimulation = () => {
setCountryData(initialCountryData);
setGlobalData({
gdpImpact: 0,
tradeVolume: 0
});
setHasRun(false);
setResults(null);
};
// Run simulation
const runSimulation = () => {
// Deep copy initial country data
const newCountryData = JSON.parse(JSON.stringify(initialCountryData));
let globalGdpImpact = 0;
let globalTradeVolumeChange = 0;
// Apply resource control factors first
// China blocking mineral refinement
if (chinaRefinementBlock > 0) {
// Reduce US access to refined materials
newCountryData.us.refinedMaterialsAccess -= chinaRefinementBlock * 0.7;
// Also impacts Japan
newCountryData.japan.refinedMaterialsAccess -= chinaRefinementBlock * 0.5;
// Impact on US tech manufacturing
newCountryData.us.techManufacturingCapacity -= chinaRefinementBlock * 0.4;
newCountryData.us.consumerPrices += chinaRefinementBlock * 0.1;
// China takes a hit too, but less severe
newCountryData.china.gdpGrowth -= chinaRefinementBlock * 0.01;
// China may benefit strategically though
newCountryData.china.techManufacturingCapacity += chinaRefinementBlock * 0.1;
}
// Ukraine giving mineral rights to EU
if (ukraineMineralsToEU > 0) {
// EU gains access
newCountryData.eu.ukrainianMineralAccess += ukraineMineralsToEU * 0.5;
// EU manufacturing boost
newCountryData.eu.techManufacturingCapacity += ukraineMineralsToEU * 0.2;
newCountryData.eu.gdpGrowth += ukraineMineralsToEU * 0.01;
// US may lose some access
newCountryData.us.rareMineralAccess -= ukraineMineralsToEU * 0.05;
}
// Russia controlling US rare minerals
if (russiaControlsUSMinerals > 0) {
// Major impact on US access
newCountryData.us.rareMineralAccess -= russiaControlsUSMinerals * 0.8;
// Russia benefits economically
newCountryData.russia.gdpGrowth += russiaControlsUSMinerals * 0.05;
newCountryData.russia.tradeBalance += russiaControlsUSMinerals * 1.5;
// Secondary effects on US economy
newCountryData.us.gdpGrowth -= russiaControlsUSMinerals * 0.03;
newCountryData.us.consumerPrices += russiaControlsUSMinerals * 0.15;
newCountryData.us.manufacturing -= russiaControlsUSMinerals * 0.2;
// China might benefit from US weakness
newCountryData.china.gdpGrowth += russiaControlsUSMinerals * 0.01;
}
// Apply US tariffs
// US effects - now modified by resource factors
const usResourceFactor = (newCountryData.us.refinedMaterialsAccess + newCountryData.us.rareMineralAccess) / 200;
newCountryData.us.imports *= (1 - (usTariffRate * 0.005)); // Reduce imports
newCountryData.us.exports *= (1 - (0.003 * (100 - newCountryData.us.techManufacturingCapacity)));
newCountryData.us.tradeBalance = newCountryData.us.exports - newCountryData.us.imports;
// Manufacturing boost reduced if resources limited
newCountryData.us.manufacturing += usTariffRate * 0.04 * usResourceFactor;
// Price increases higher if resources limited
newCountryData.us.consumerPrices += usTariffRate * 0.06 * (2 - usResourceFactor);
// GDP impact worse if resources limited
newCountryData.us.gdpGrowth -= usTariffRate * 0.01 * (2 - usResourceFactor);
// Employment impact - worse if manufacturing capability reduced
newCountryData.us.employment += (usTariffRate * 0.01) * (2 - usResourceFactor);
// Target countries retaliation effects (China primarily)
const retaliationFactor = retaliationLevel / 100;
// China effects - now with refinement leverage
const chinaLeverageFactor = 1 + (chinaRefinementBlock / 100);
newCountryData.china.exports *= (1 - (usTariffRate * 0.006 * retaliationFactor / chinaLeverageFactor));
newCountryData.china.imports -= usTariffRate * 3 * retaliationFactor;
newCountryData.china.tradeBalance = newCountryData.china.exports - newCountryData.china.imports;
newCountryData.china.gdpGrowth -= usTariffRate * 0.03 * retaliationFactor / chinaLeverageFactor;
newCountryData.china.manufacturing -= usTariffRate * 0.04 * retaliationFactor / chinaLeverageFactor;
newCountryData.china.employment += usTariffRate * 0.02 * retaliationFactor / chinaLeverageFactor;
newCountryData.china.consumerPrices += usTariffRate * 0.02 * retaliationFactor;
// EU effects - now with Ukrainian mineral benefits
const euResourceBoost = 1 + (ukraineMineralsToEU / 200);
const euFactor = retaliationFactor * 0.7; // EU less directly affected
newCountryData.eu.exports *= (1 - (usTariffRate * 0.003 * euFactor)) * euResourceBoost;
newCountryData.eu.tradeBalance = newCountryData.eu.exports - newCountryData.eu.imports;
newCountryData.eu.gdpGrowth = (newCountryData.eu.gdpGrowth - usTariffRate * 0.015 * euFactor) * euResourceBoost;
newCountryData.eu.manufacturing = (newCountryData.eu.manufacturing - usTariffRate * 0.02 * euFactor) * euResourceBoost;
newCountryData.eu.employment += usTariffRate * 0.01 * euFactor / euResourceBoost;
newCountryData.eu.consumerPrices += usTariffRate * 0.01 * euFactor;
// Japan effects - now with China refinement impact
const japanResourceFactor = newCountryData.japan.refinedMaterialsAccess / 100;
const japanFactor = retaliationFactor * 0.6;
newCountryData.japan.exports *= (1 - (usTariffRate * 0.004 * japanFactor)) * japanResourceFactor;
newCountryData.japan.tradeBalance = newCountryData.japan.exports - newCountryData.japan.imports;
newCountryData.japan.gdpGrowth -= usTariffRate * 0.02 * japanFactor / japanResourceFactor;
newCountryData.japan.manufacturing -= usTariffRate * 0.03 * japanFactor / japanResourceFactor;
newCountryData.japan.employment += usTariffRate * 0.015 * japanFactor / japanResourceFactor;
newCountryData.japan.consumerPrices += usTariffRate * 0.015 * japanFactor * (2 - japanResourceFactor);
// Russia effects - new from controlling minerals
if (russiaControlsUSMinerals > 0) {
newCountryData.russia.exports += russiaControlsUSMinerals * 1.5;
newCountryData.russia.tradeBalance = newCountryData.russia.exports - newCountryData.russia.imports;
// Positive GDP boost from resource control
newCountryData.russia.gdpGrowth += russiaControlsUSMinerals * 0.04;
newCountryData.russia.employment -= russiaControlsUSMinerals * 0.01; // Reduced unemployment
}
// Australia effects depend on alignment and mineral situation
let australiaFactor;
if (australiaPosition === 'us-aligned') {
australiaFactor = 0.3; // Less impact if aligned with US
// Might even get some benefit from trade diversion
newCountryData.australia.resourceExports += usTariffRate * 0.02;
// Australia can partially offset US mineral shortages
if (russiaControlsUSMinerals > 0 || chinaRefinementBlock > 0) {
newCountryData.australia.resourceExports +=
(russiaControlsUSMinerals + chinaRefinementBlock) * 0.15;
newCountryData.australia.gdpGrowth +=
(russiaControlsUSMinerals + chinaRefinementBlock) * 0.02;
// Help US recovery somewhat
newCountryData.us.rareMineralAccess += 20;
}
} else if (australiaPosition === 'neutral') {
australiaFactor = 0.5; // Moderate impact if neutral
} else {
australiaFactor = 0.8; // Stronger impact if China-aligned
newCountryData.australia.resourceExports -= usTariffRate * 0.03;
// Boost to China if Australia aligns with them
newCountryData.china.rareMineralReserves += 15;
newCountryData.china.gdpGrowth += 0.2;
}
newCountryData.australia.exports *= (1 - (usTariffRate * 0.002 * australiaFactor));
newCountryData.australia.tradeBalance = newCountryData.australia.exports - newCountryData.australia.imports;
newCountryData.australia.gdpGrowth -= usTariffRate * 0.01 * australiaFactor;
newCountryData.australia.employment += usTariffRate * 0.01 * australiaFactor;
newCountryData.australia.consumerPrices += usTariffRate * 0.01 * australiaFactor;
// Calculate global impacts
for (const country in newCountryData) {
const initial = initialCountryData[country];
const current = newCountryData[country];
// GDP impact
const gdpChange = current.gdp * (current.gdpGrowth - initial.gdpGrowth) / 100;
globalGdpImpact += gdpChange;
// Trade volume change
const exportsChange = current.exports - initial.exports;
const importsChange = current.imports - initial.imports;
globalTradeVolumeChange += (exportsChange + importsChange) / 2;
}
// Generate results data for charts
const gdpResults = Object.entries(newCountryData).map(([key, country]) => ({
name: country.name,
originalGrowth: initialCountryData[key].gdpGrowth,
newGrowth: country.gdpGrowth,
change: country.gdpGrowth - initialCountryData[key].gdpGrowth
}));
const tradeResults = Object.entries(newCountryData).map(([key, country]) => ({
name: country.name,
originalBalance: initialCountryData[key].tradeBalance,
newBalance: country.tradeBalance,
change: country.tradeBalance - initialCountryData[key].tradeBalance
}));
const priceResults = Object.entries(newCountryData).map(([key, country]) => ({
name: country.name,
priceIncrease: country.consumerPrices
}));
setResults({
gdp: gdpResults,
trade: tradeResults,
prices: priceResults
});
setCountryData(newCountryData);
setGlobalData({
gdpImpact: globalGdpImpact,
tradeVolume: globalTradeVolumeChange
});
setHasRun(true);
};
return (
<div className="flex flex-col w-full p-4 space-y-6">
<div className="p-6 bg-gray-100 rounded-lg shadow">
<h2 className="text-2xl font-bold mb-4">Tariff War Simulation Controls</h2>
<div className="space-y-4">
<div className="flex items-center space-x-4">
<label className="w-64">US Initial Tariff Increase:</label>
<input
type="range"
min="5"
max="50"
value={usTariffRate}
onChange={(e) => setUsTariffRate(parseInt(e.target.value))}
className="w-64"
/>
<span className="w-16">{usTariffRate}%</span>
</div>
<div className="flex items-center space-x-4">
<label className="w-64">Retaliation Level:</label>
<input
type="range"
min="0"
max="150"
value={retaliationLevel}
onChange={(e) => setRetaliationLevel(parseInt(e.target.value))}
className="w-64"
/>
<span className="w-16">{retaliationLevel}%</span>
</div>
<div className="flex items-center space-x-4">
<label className="w-64">Australia's Trade Position:</label>
<select
value={australiaPosition}
onChange={(e) => setAustraliaPosition(e.target.value)}
className="p-2 border rounded"
>
<option value="us-aligned">US-Aligned (Apply Similar Tariffs)</option>
<option value="neutral">Neutral (No Tariffs)</option>
<option value="china-aligned">China-Aligned (Counter Tariffs)</option>
</select>
</div>
<div className="flex items-center space-x-4">
<label className="w-64">China Mineral Refinement Block:</label>
<input
type="range"
min="0"
max="100"
value={chinaRefinementBlock}
onChange={(e) => setChinaRefinementBlock(parseInt(e.target.value))}
className="w-64"
/>
<span className="w-16">{chinaRefinementBlock}%</span>
</div>
<div className="flex items-center space-x-4">
<label className="w-64">Ukraine Mineral Rights to EU:</label>
<input
type="range"
min="0"
max="100"
value={ukraineMineralsToEU}
onChange={(e) => setUkraineMineralsToEU(parseInt(e.target.value))}
className="w-64"
/>
<span className="w-16">{ukraineMineralsToEU}%</span>
</div>
<div className="flex items-center space-x-4">
<label className="w-64">Russia Controls US Rare Minerals:</label>
<input
type="range"
min="0"
max="100"
value={russiaControlsUSMinerals}
onChange={(e) => setRussiaControlsUSMinerals(parseInt(e.target.value))}
className="w-64"
/>
<span className="w-16">{russiaControlsUSMinerals}%</span>
</div>
<div className="flex space-x-4 mt-6">
<button
onClick={runSimulation}
className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
>
Run Simulation
</button>
<button
onClick={resetSimulation}
className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600"
>
Reset
</button>
</div>
</div>
</div>
{hasRun && results && (
<div className="space-y-8">
<div className="grid grid-cols-2 gap-6">
<div className="p-6 bg-white rounded-lg shadow">
<h3 className="text-xl font-bold mb-4">GDP Growth Impacts</h3>
<ResponsiveContainer width="100%" height={300}>
<BarChart data={results.gdp}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis domain={[-1, 6]} />
<Tooltip />
<Legend />
<Bar dataKey="originalGrowth" fill="#8884d8" name="Original Growth %" />
<Bar dataKey="newGrowth" fill="#82ca9d" name="New Growth %" />
</BarChart>
</ResponsiveContainer>
<p className="mt-4 text-lg font-semibold">
Global GDP Impact: {globalData.gdpImpact.toFixed(1)} billion USD
</p>
</div>
<div className="p-6 bg-white rounded-lg shadow">
<h3 className="text-xl font-bold mb-4">Trade Balance Changes</h3>
<ResponsiveContainer width="100%" height={300}>
<BarChart data={results.trade}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="originalBalance" fill="#8884d8" name="Original Balance (B$)" />
<Bar dataKey="newBalance" fill="#82ca9d" name="New Balance (B$)" />
</BarChart>
</ResponsiveContainer>
<p className="mt-4 text-lg font-semibold">
Global Trade Volume Change: {globalData.tradeVolume.toFixed(1)} billion USD
</p>
</div>
</div>
<div className="grid grid-cols-3 gap-6">
{Object.entries(countryData).map(([key, country]) => (
<div key={key} className="p-6 bg-white rounded-lg shadow">
<h3 className="text-xl font-bold mb-4">{country.name}</h3>
<div className="space-y-2">
<div className="flex justify-between">
<span>GDP Growth:</span>
<span className={country.gdpGrowth < initialCountryData[key].gdpGrowth ? 'text-red-500' : 'text-green-500'}>
{country.gdpGrowth.toFixed(1)}%
</span>
</div>
<div className="flex justify-between">
<span>Inflation:</span>
<span>{country.inflation.toFixed(1)}%</span>
</div>
<div className="flex justify-between">
<span>Unemployment:</span>
<span>{country.employment.toFixed(1)}%</span>
</div>
<div className="flex justify-between">
<span>Trade Balance:</span>
<span>${country.tradeBalance.toFixed(1)}B</span>
</div>
<div className="flex justify-between">
<span>Consumer Prices:</span>
<span className={country.consumerPrices > 0 ? 'text-red-500' : ''}>
+{country.consumerPrices.toFixed(1)}%
</span>
</div>
<div className="flex justify-between">
<span>Manufacturing:</span>
<span className={country.manufacturing > 0 ? 'text-green-500' : 'text-red-500'}>
{country.manufacturing > 0 ? '+' : ''}{country.manufacturing.toFixed(1)}%
</span>
</div>
{/* Country-specific resource metrics */}
{key === 'us' && (
<>
<div className="flex justify-between">
<span>Rare Mineral Access:</span>
<span className={country.rareMineralAccess < 100 ? 'text-red-500' : ''}>
{country.rareMineralAccess.toFixed(1)}%
</span>
</div>
<div className="flex justify-between">
<span>Refined Materials:</span>
<span className={country.refinedMaterialsAccess < 100 ? 'text-red-500' : ''}>
{country.refinedMaterialsAccess.toFixed(1)}%
</span>
</div>
<div className="flex justify-between">
<span>Tech Manufacturing:</span>
<span className={country.techManufacturingCapacity < 100 ? 'text-red-500' : 'text-green-500'}>
{country.techManufacturingCapacity.toFixed(1)}%
</span>
</div>
</>
)}
{key === 'china' && (
<>
<div className="flex justify-between">
<span>Refinement Capacity:</span>
<span className={country.mineralRefinementCapacity > 100 ? 'text-green-500' : ''}>
{country.mineralRefinementCapacity.toFixed(1)}%
</span>
</div>
<div className="flex justify-between">
<span>Rare Mineral Reserves:</span>
<span className={country.rareMineralReserves > 100 ? 'text-green-500' : ''}>
{country.rareMineralReserves.toFixed(1)}%
</span>
</div>
</>
)}
{key === 'eu' && (
<>
<div className="flex justify-between">
<span>Ukrainian Minerals:</span>
<span className={country.ukrainianMineralAccess > 100 ? 'text-green-500' : ''}>
{country.ukrainianMineralAccess.toFixed(1)}%
</span>
</div>
</>
)}
{key === 'russia' && (
<>
<div className="flex justify-between">
<span>Resource Control:</span>
<span className={country.resourceControl > 100 ? 'text-green-500' : ''}>
{country.resourceControl.toFixed(1)}%
</span>
</div>
</>
)}
{key === 'australia' && (
<div className="flex justify-between">
<span>Resource Exports:</span>
<span>${country.resourceExports.toFixed(1)}B</span>
</div>
)}
</div>
</div>
))}
</div>
<div className="p-6 bg-white rounded-lg shadow">
<h3 className="text-xl font-bold mb-4">Consumer Price Impacts</h3>
<ResponsiveContainer width="100%" height={300}>
<BarChart data={results.prices}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Bar dataKey="priceIncrease" fill="#ff8042" name="Price Increase %" />
</BarChart>
</ResponsiveContainer>
</div>
</div>
)}
{!hasRun && (
<div className="grid grid-cols-3 gap-6">
{Object.entries(countryData).map(([key, country]) => (
<div key={key} className="p-6 bg-white rounded-lg shadow">
<h3 className="text-xl font-bold mb-4">{country.name}</h3>
<div className="space-y-2">
<div className="flex justify-between">
<span>GDP Growth:</span>
<span>{country.gdpGrowth.toFixed(1)}%</span>
</div>
<div className="flex justify-between">
<span>Inflation:</span>
<span>{country.inflation.toFixed(1)}%</span>
</div>
<div className="flex justify-between">
<span>Unemployment:</span>
<span>{country.employment.toFixed(1)}%</span>
</div>
<div className="flex justify-between">
<span>Trade Balance:</span>
<span>${country.tradeBalance}B</span>
</div>
<div className="flex justify-between">
<span>Consumer Prices:</span>
<span>+0.0%</span>
</div>
<div className="flex justify-between">
<span>Manufacturing:</span>
<span>+0.0%</span>
</div>
{key === 'australia' && (
<div className="flex justify-between">
<span>Resource Exports:</span>
<span>${country.resourceExports}B</span>
</div>
)}
</div>
</div>
))}
</div>
)}
<div className="grid grid-cols-2 gap-6">
<div className="p-6 bg-white rounded-lg shadow">
<h3 className="text-xl font-bold mb-4">Global GDP Impact</h3>
{hasRun ? (
<p className="text-lg">
{globalData.gdpImpact > 0
? `Overall positive impact of $${globalData.gdpImpact.toFixed(1)} billion`
: `Overall negative impact of $${Math.abs(globalData.gdpImpact).toFixed(1)} billion`}
</p>
) : (
<p>Run the simulation to see results</p>
)}
</div>
<div className="p-6 bg-white rounded-lg shadow">
<h3 className="text-xl font-bold mb-4">Global Trade Volume</h3>
{hasRun ? (
<p className="text-lg">
{globalData.tradeVolume > 0
? `Net trade increase of $${globalData.tradeVolume.toFixed(1)} billion`
: `Net trade decrease of $${Math.abs(globalData.tradeVolume).toFixed(1)} billion`}
</p>
) : (
<p>Run the simulation to see results</p>
)}
</div>
</div>
</div>
);
};
export default TariffWarSimulation;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment