add support for popular vote elections and add nk 2019

This commit is contained in:
Elijah R 2024-06-18 21:57:56 -04:00
parent f073c48cc7
commit e8183499c6
17 changed files with 235 additions and 6 deletions

66
assets/NorthKorea2019.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 175 KiB

15
src/VoteType.ts Normal file
View file

@ -0,0 +1,15 @@
enum VoteType {
Electoral,
Popular,
}
export function VoteTypeString(voteType: VoteType) {
switch (voteType) {
case VoteType.Electoral:
return "Electoral Votes";
case VoteType.Popular:
return "Votes";
}
}
export default VoteType;

View file

@ -1,7 +1,10 @@
import VoteType from './VoteType.js';
export default interface Election {
title : string,
shortname : string,
description : string,
voteType : VoteType,
svg: string,
candidates : {
name : string,
@ -10,7 +13,8 @@ export default interface Election {
}[],
states : {
[key : string] : {
electoralVotes : number,
electoralVotes? : number | undefined,
population? : number | undefined,
odds : {
[key : string] : number
}

View file

@ -1,10 +1,12 @@
import { readFile } from "node:fs/promises";
import Election from "../election.js";
import VoteType from "../VoteType.js";
const Presidential_1789 = {
title: "1789 United States Presidential Election Simulator",
shortname: "1789",
description: "The CalubViem Press has rewritten history and called the 1789 United States Presidential Election for $WINNER!",
voteType: VoteType.Electoral,
svg: await readFile("assets/ElectoralCollege1789.svg", "utf-8"),
candidates: [
{

View file

@ -1,10 +1,12 @@
import { readFile } from "node:fs/promises";
import Election from "../election.js";
import VoteType from "../VoteType.js";
const Presidential_1848 = {
title: "1848 United States Presidential Election Simulator",
shortname: "1848",
description: "The CalubViem Press has rewritten history and called the 1848 United States Presidential Election for $WINNER!",
voteType: VoteType.Electoral,
svg: await readFile("assets/ElectoralCollege1848.svg", "utf-8"),
candidates: [
{

View file

@ -1,10 +1,12 @@
import { readFile } from "node:fs/promises";
import Election from "../election.js";
import VoteType from "../VoteType.js";
const Presidential_1864 = {
title: "1864 United States Presidential Election Simulator",
shortname: "1864",
description: "The CalubViem Press has rewritten history and called the 1864 United States Presidential Election for $WINNER!",
voteType: VoteType.Electoral,
svg: await readFile("assets/ElectoralCollege1864.svg", "utf-8"),
candidates: [
{

View file

@ -1,10 +1,12 @@
import { readFile } from "node:fs/promises";
import Election from "../election.js";
import VoteType from "../VoteType.js";
const Presidential_1968 = {
title: "1968 United States Presidential Election Simulator",
shortname: "1968",
description: "The CalubViem Press has rewritten history and called the 1968 United States Presidential Election for $WINNER!",
voteType: VoteType.Electoral,
svg: await readFile("assets/ElectoralCollege1968.svg", "utf-8"),
candidates: [
{

View file

@ -1,10 +1,12 @@
import { readFile } from "node:fs/promises";
import Election from "../election.js";
import VoteType from "../VoteType.js";
const Presidential_1968 = {
title: "1968 United States Presidential Election Simulator",
shortname: "1968",
description: "The CalubViem Press has rewritten history and called the 1968 United States Presidential Election for $WINNER!",
voteType: VoteType.Electoral,
svg: await readFile("assets/ElectoralCollege1968.svg", "utf-8"),
candidates: [
{

View file

@ -1,10 +1,12 @@
import { readFile } from "node:fs/promises";
import Election from "../election.js";
import VoteType from "../VoteType.js";
const Presidential_1992 = {
title: "1992 United States Presidential Election Simulator",
shortname: "1992",
description: "The CalubViem Press has rewritten history and called the 1992 United States Presidential Election for $WINNER!",
voteType: VoteType.Electoral,
svg: await readFile("assets/ElectoralCollege1992.svg", "utf-8"),
candidates: [
{

View file

@ -1,10 +1,12 @@
import { readFile } from "node:fs/promises";
import Election from "../election.js";
import VoteType from "../VoteType.js";
const Presidential_2000 = {
title: "2000 United States Presidential Election Simulator",
shortname: "2000",
description: "The CalubViem Press has rewritten history and called the 2000 United States Presidential Election for $WINNER!",
voteType: VoteType.Electoral,
svg: await readFile("assets/ElectoralCollege1992.svg", "utf-8"),
candidates: [
{

View file

@ -1,10 +1,12 @@
import { readFile } from "node:fs/promises";
import Election from "../election.js";
import VoteType from "../VoteType.js";
const Presidential_2004 = {
title: "2004 United States Presidential Election Simulator",
shortname: "2004",
description: "The CalubViem Press has rewritten history and called the 2004 United States Presidential Election for $WINNER!",
voteType: VoteType.Electoral,
svg: await readFile("assets/ElectoralCollege2004.svg", "utf-8"),
candidates: [
{

View file

@ -1,10 +1,12 @@
import { readFile } from "node:fs/promises";
import Election from "../election.js";
import VoteType from "../VoteType.js";
const Presidential_2008 = {
title: "2008 United States Presidential Election Simulator",
shortname: "2008",
description: "The CalubViem Press has rewritten history and called the 2008 United States Presidential Election for $WINNER!",
voteType: VoteType.Electoral,
svg: await readFile("assets/ElectoralCollege2008.svg", "utf-8"),
candidates: [
{

View file

@ -1,10 +1,12 @@
import { readFile } from "node:fs/promises";
import Election from "../election.js";
import VoteType from "../VoteType.js";
const Presidential_2024 = {
title: "2024 United States Presidential Election Simulator",
shortname: "2024",
description: "The CalubViem Press has called the 2024 United States Presidential Election for $WINNER!",
voteType: VoteType.Electoral,
svg: await readFile("assets/ElectoralCollege2024.svg", "utf-8"),
candidates: [
{

94
src/elections/NK2019.ts Normal file
View file

@ -0,0 +1,94 @@
import { readFile } from "fs/promises";
import VoteType from "../VoteType.js";
import Election from "../election.js"
const NK_2019 = {
title: "2019 North Korean Supreme People's Assembly Election",
shortname: "nk2019",
description: "The Central Election Committee of the Democratic People's Republic of Korea has called the election for $WINNER!",
voteType: VoteType.Popular,
svg: await readFile("assets/NorthKorea2019.svg", "utf-8"),
candidates: [
{
name: "Kim Jong-Un",
party: "Democratic Front FTRF",
color: "#FF0000",
},
],
states: {
"KP-01": {
population: 3157538,
odds: {
"Democratic Front FTRF": 1,
}
},
"KP-02": {
population: 4051696,
odds: {
"Democratic Front FTRF": 1,
}
},
"KP-03": {
population: 12191,
odds: {
"Democratic Front FTRF": 1,
}
},
"KP-04": {
population: 1299830,
odds: {
"Democratic Front FTRF": 1,
}
},
"KP-05": {
population: 8450,
odds: {
"Democratic Front FTRF": 1,
}
},
"KP-06": {
population: 8154,
odds: {
"Democratic Front FTRF": 1,
}
},
"KP-07": {
population: 11255,
odds: {
"Democratic Front FTRF": 1,
}
},
"KP-08": {
population: 18970,
odds: {
"Democratic Front FTRF": 1,
}
},
"KP-09": {
population: 2327362,
odds: {
"Democratic Front FTRF": 1,
}
},
"KP-10": {
population: 719269,
odds: {
"Democratic Front FTRF": 1,
}
},
"KP-13": {
population: 205000,
odds: {
"Democratic Front FTRF": 1,
}
},
"KP-14": {
population: 983660,
odds: {
"Democratic Front FTRF": 1,
}
},
}
}
export default NK_2019 as Election;

View file

@ -8,8 +8,10 @@ import Presidential_2000 from "./2000.js";
import Presidential_2004 from "./2004.js";
import Presidential_2008 from "./2008.js";
import Presidential_2024 from "./2024.js";
import NK_2019 from "./NK2019.js";
const Elections = {
// US
"1789": Presidential_1789,
"1848": Presidential_1848,
"1864": Presidential_1864,
@ -18,7 +20,9 @@ const Elections = {
"2000": Presidential_2000,
"2004": Presidential_2004,
"2008": Presidential_2008,
"2024": Presidential_2024
"2024": Presidential_2024,
// Other
"nk2019": NK_2019,
}
export default (Elections as {[key : string] : Election})

View file

@ -5,6 +5,7 @@ import {MakePrediction} from "./predictor.js";
import Election from "./election.js";
import Presidential_2024 from "./elections/2024.js";
import Elections from "./elections/elections.js";
import VoteType, { VoteTypeString } from "./VoteType.js";
const configraw = fs.readFileSync("config.json", "utf-8");
const config = JSON.parse(configraw);
@ -100,7 +101,7 @@ if (!config.token) {
.addFields(result.candidates.map(c => {
return {
name: `${(result.winner === c.name ? ":white_check_mark:" : "")} ${c.name} (${c.party})`,
value: `${c.votes} Electoral Votes`,
value: `${c.votes} ${VoteTypeString(election.voteType)}${election.voteType === VoteType.Popular ? ` (${((c.votes / result.totalVotes) * 100).toFixed(2)}%)` : ""}`,
inline: true
}
}))

View file

@ -3,6 +3,7 @@ import { SVG, registerWindow } from '@svgdotjs/svg.js'
import sharp from "sharp";
import crypto from "crypto";
import Election from "./election.js";
import VoteType from './VoteType.js';
export function MakePrediction(election : Election) : Promise<Prediction> {
return new Promise(async res => {
@ -24,8 +25,17 @@ export function MakePrediction(election : Election) : Promise<Prediction> {
if (Object.keys(election.states[state].odds).every(p => election.states[state].odds[p] <= 0))
for (const candidate of election.candidates)
election.states[state].odds[candidate.party] = 1;
var winner = await weightedRand(election.states[state].odds);
pred.candidates.find((c : any) => c.party === winner).votes += election.states[state].electoralVotes;
var winner = "";
if (election.voteType === VoteType.Electoral) {
winner = await weightedRand(election.states[state].odds);
pred.candidates.find((c : any) => c.party === winner).votes += election.states[state].electoralVotes;
} else if (election.voteType === VoteType.Popular) {
var votes = await getVotes(election.states[state].population!, election.states[state].odds);
winner = Object.keys(votes).sort((a, b) => votes[b] - votes[a])[0];
for (const candidate of election.candidates) {
pred.candidates.find((c : any) => c.party === candidate.party).votes += votes[candidate.party];
}
}
// @ts-ignore
draw.find(`#${state}`).fill(election.candidates.find((c : any) => c.party === winner).color);
}
@ -33,6 +43,7 @@ export function MakePrediction(election : Election) : Promise<Prediction> {
var s = sharp(Buffer.from(draw.svg()));
var png = await s.png().toBuffer();
pred.winner = pred.candidates.sort((a : any, b : any) => b.votes - a.votes)[0].name;
pred.totalVotes = pred.candidates.reduce((a : any, b : any) => a + b.votes, 0);
pred.svg = draw.svg();
pred.png = png;
res(pred);
@ -44,9 +55,10 @@ export interface Prediction {
candidates : {
name : string,
party : string,
votes : string,
votes : number,
}[],
winner: string,
totalVotes: number,
svg: string,
png: Buffer,
}
@ -71,4 +83,17 @@ export async function weightedRand(spec : { [key : string] : number }) : Promise
}
}
return table[await betterRandom(0, table.length)];
}
export async function getVotes(population: number, spec: { [key: string]: number }) : Promise<{ [key: string]: number }>{
// split the population into 100 chunks
var chunk = population / 100;
var votes = {} as { [key: string]: number };
for (var c in spec) votes[c] = 0;
for (var i = 0; i < 100; i++) {
var winner = await weightedRand(spec);
votes[winner] += chunk;
}
for (var c in votes) votes[c] = Math.round(votes[c]);
return votes;
}