chore: small improvements to the slightly smart cpu

This commit is contained in:
Andre Henriques 2025-04-04 11:54:45 +01:00
parent 7a8b5e198d
commit 9904439a78

View File

@ -1,96 +1,102 @@
import { Play, PLAYER_OPTIONS, PlayerAction } from "../game";
export type CPUNAME = "CPU Random" | "CPU Always Rock" | "Slightly Smart";
export const CPUS_NAMES: CPUNAME[] = ["CPU Random", "CPU Always Rock", "Slightly Smart"];
export const CPUS_NAMES: CPUNAME[] = [
"CPU Random",
"CPU Always Rock",
"Slightly Smart",
];
export const CPU_FUNCTION_MAPING: Record<CPUNAME, (plays: Play[]) => PlayerAction> = {
"CPU Random": CpuRandom,
"CPU Always Rock": () => 'Rock',
"Slightly Smart": CpuSlightlySmart,
}
export const CPU_FUNCTION_MAPING: Record<
CPUNAME,
(plays: Play[]) => PlayerAction
> = {
"CPU Random": CpuRandom,
"CPU Always Rock": () => "Rock",
"Slightly Smart": CpuSlightlySmart,
};
export const CPU_FUNCTION_MAPING_END: ((plays: Play[], user: string) => void)[] = [
CpuSlightlySmartEnd
]
export const CPU_FUNCTION_MAPING_END: ((
plays: Play[],
user: string,
) => void)[] = [CpuSlightlySmartEnd];
function CpuRandom(): PlayerAction {
return PLAYER_OPTIONS[Math.floor(Math.random() * PLAYER_OPTIONS.length)];
return PLAYER_OPTIONS[Math.floor(Math.random() * PLAYER_OPTIONS.length)];
}
//
// CPU slightly Smart
//
// Keeps track of the all users plays and then use that to modify the chance
// of an action to be played. The action will be weigthed based on the actions
// that are likely to win agains the trend of what players have been playing
//
const CpuSlightlySmartValue = PLAYER_OPTIONS.reduce(
(acc, a) => {
acc[a] = 1;
return acc;
},
{} as Record<PlayerAction, number>,
);
function CpuSlightlySmart(plays: Play[]): PlayerAction {
const turn = plays.length;
const turn = plays.length;
function runTurn(extractFromStorage: 'st' | 'nd' | 'rd') {
let base = PLAYER_OPTIONS.reduce((acc, a) => {
acc[a] = 1;
return acc;
}, {} as Record<PlayerAction, number>);
let base = CpuSlightlySmartValue;
try {
const newBase = JSON.parse(
localStorage.getItem("slightly-smart-cpu-data") ?? "",
);
base = { ...base, ...newBase[turn] };
} catch {
// Do nothing use the default
}
try {
const newBase = JSON.parse(localStorage.getItem('slightly-smart-cpu-data') ?? '');
base = { ...base, ...newBase[extractFromStorage] };
} catch {
// Do nothing use the default
}
const sum = PLAYER_OPTIONS.reduce((acc, a) => acc + base[a], 0);
const choise = Math.floor(Math.random() * sum);
let runningCount = 0;
let sum = PLAYER_OPTIONS.reduce((acc, a) => acc + base[a], 0);
const choise = Math.floor(Math.random() * sum);
let runningCount = 0;
for (const a of PLAYER_OPTIONS) {
const newSum = runningCount + base[a];
if (choise > runningCount && choise < newSum) {
return a;
}
runningCount = newSum;
}
for (const a of PLAYER_OPTIONS) {
let newSum = runningCount + base[a];
if (choise > runningCount && choise < newSum) {
return a;
}
runningCount = newSum;
}
// it shouldn't reach here but if it does just return something random
return undefined;
}
if (turn === 0) {
const res = runTurn('st')
if (res !== undefined) return res;
} else if (turn === 1) {
const res = runTurn('nd')
if (res !== undefined) return res;
}
else if (turn === 2) {
const res = runTurn('rd')
if (res !== undefined) return res;
}
else throw 'unrechable';
return PLAYER_OPTIONS[Math.floor(Math.random() * PLAYER_OPTIONS.length)];
return PLAYER_OPTIONS[Math.floor(Math.random() * PLAYER_OPTIONS.length)];
}
function CpuSlightlySmartEnd(plays: Play[]) {
let storageData: {
st: Partial<Record<PlayerAction, number>>
nd: Partial<Record<PlayerAction, number>>
rd: Partial<Record<PlayerAction, number>>
} = { st: {}, nd: {}, rd: {} };
try {
// It's ok for this to error
storageData = JSON.parse(localStorage.getItem('slightly-smart-cpu-data') ?? '');
} catch {
// do nothing just goto the default value
}
let storageData: Partial<Record<PlayerAction, number>>[] = [{}, {}, {}];
try {
// It's ok for this to error
storageData = JSON.parse(
localStorage.getItem("slightly-smart-cpu-data") ?? "",
);
// Handle the old version of the data
if (!Array.isArray(storageData)) {
storageData = [{}, {}, {}];
}
} catch {
// do nothing just goto the default value
}
const ResultReverseMappings: Record<PlayerAction, PlayerAction[]> = {
"Scissors": ["Rock", "Spock"],
"Paper": ["Scissors", "Lizard"],
"Rock": ["Paper", "Spock"],
"Lizard": ["Scissors", "Rock"],
"Spock": ["Paper", "Lizard"],
};
const ResultReverseMappings: Record<PlayerAction, PlayerAction[]> = {
Scissors: ["Rock", "Spock"],
Paper: ["Scissors", "Lizard"],
Rock: ["Paper", "Spock"],
Lizard: ["Scissors", "Rock"],
Spock: ["Paper", "Lizard"],
};
ResultReverseMappings[plays[0].user].forEach((a: PlayerAction) => { storageData.st[a] = (storageData.st[a] ?? 0) + 1; })
ResultReverseMappings[plays[1].user].forEach((a: PlayerAction) => { storageData.nd[a] = (storageData.nd[a] ?? 0) + 1; })
ResultReverseMappings[plays[2].user].forEach((a: PlayerAction) => { storageData.rd[a] = (storageData.rd[a] ?? 0) + 1; })
for (let i = 0; i < 3; i++) {
ResultReverseMappings[plays[i].user].forEach((a: PlayerAction) => {
storageData[i][a] = (storageData[i][a] ?? 0) + 1;
});
}
localStorage.setItem('slightly-smart-cpu-data', JSON.stringify(storageData));
localStorage.setItem(
"slightly-smart-cpu-data",
JSON.stringify(storageData),
);
}