Last active
July 12, 2017 00:34
-
-
Save kirillseva/961fa1f5b5d64254e0117caf11b64b27 to your computer and use it in GitHub Desktop.
People in a room giving dollars to random others
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
library(animation) | |
library(gganimate) | |
library(ggplot2) | |
library(ggthemr) | |
library(magrittr) | |
library(tweenr) | |
library(reshape2) | |
## Configurable parameters | |
NUM_PEOPLE = 45 | |
INITIAL_BANK = 45 | |
INTEREST_RATE = 0.1 | |
ROUNDS = 1500 | |
RENT = 1 | |
## Code that uses these parameters to generate animation | |
give_money <- function(state) { | |
payment_schedule <- sample(seq_along(state$wallets), replace = TRUE) | |
for (i in seq_along(state$wallets)) { | |
available_cash <- state$wallets[i] | |
payout <- RENT | |
if (available_cash < payout) { | |
# have to borrow | |
their_debt <- state$loans[payment_schedule[i], i] | |
if (their_debt > 0) { | |
# if they owe me - settle that first | |
covered_by_loan <- min(their_debt, payout) | |
state$loans[payment_schedule[i], i] %<>% { . - covered_by_loan} | |
payout %<>% { . - covered_by_loan } | |
} | |
# if the whole payout is covered - nothing left to do | |
if (payout > 0) { | |
if (available_cash < payout) { | |
# still not enough money! | |
# if there's still something left to pay for - borrow | |
loan_amount <- payout - available_cash | |
state$wallets[i] <- 0 | |
state$wallets[payment_schedule[i]] %<>% { . + available_cash } | |
state$loans[i, payment_schedule[i]] %<>% { . + loan_amount } | |
} else { | |
# finally enough money to pay | |
state$wallets[i] %<>% { . - payout } | |
state$wallets[payment_schedule[i]] %<>% { . + payout } | |
} | |
} | |
} else { | |
# have enough money to pay | |
state$wallets[i] %<>% { . - payout } | |
state$wallets[payment_schedule[i]] %<>% { . + payout } | |
} | |
} | |
state | |
} | |
pay_back_the_loans <- function(state) { | |
for (x in seq_len(NROW(state$loans))) { | |
for (y in seq_len(NCOL(state$loans))) { | |
# diagonal is always 0 | |
if (y != x) { | |
debt <- state$loans[x, y] | |
available_cash <- state$wallets[x] | |
if (debt > 0 && available_cash > 0) { | |
payout <- min(available_cash, debt) | |
state$loans[x, y] %<>% { . - payout } | |
state$wallets[x] %<>% { . - payout } | |
} | |
} | |
} | |
} | |
state | |
} | |
charge_interest <- function(state) { | |
state$loans %<>% { state$loans * (1 + INTEREST_RATE) } | |
state | |
} | |
last_state <- list( | |
wallets = rep(INITIAL_BANK, NUM_PEOPLE), | |
loans = matrix(0, NUM_PEOPLE, NUM_PEOPLE) | |
) | |
message("Simulation in progress") | |
simulation <- pbapply::pblapply(seq_len(ROUNDS), function(x) { | |
# a little hack so that animation starts from initial state | |
if (x > 1) { | |
last_state <<- last_state %>% | |
charge_interest %>% | |
pay_back_the_loans %>% | |
give_money | |
} else { last_state } | |
}) | |
message("munging data") | |
wallet_history <- lapply(simulation, `[[`, "wallets") %>% | |
lapply(., function(x) { | |
setNames(as.data.frame(lapply(x, identity)), paste0("p", seq_along(x))) | |
}) | |
wallet_history <- lapply(seq_along(simulation), function(x) { | |
lapply(seq_len(NUM_PEOPLE), function(y) { | |
debt <- simulation[[x]]$loans %>% { colSums(.) - rowSums(.) } | |
data.frame( | |
cash = simulation[[x]]$wallets[y], | |
loans = debt[y] | |
) | |
}) %>% plyr::rbind.fill(.) | |
}) | |
plottable <- tween_states(wallet_history, tweenlength = 1, statelength = 1, | |
ease = c("exponential-in-out"), | |
nframes = 10) | |
plottable$player <- as.factor(paste0("p", seq_len(NUM_PEOPLE))) | |
plottable$debt_free <- (plottable$loans + plottable$cash) >= 0 | |
message("Generating charts") | |
ggthemr("flat") | |
p <- ggplot(plottable, aes(frame = .frame, x = player, y = cash+loans, fill = debt_free)) + | |
geom_bar(stat = "identity", position = "identity") + | |
guides(fill = FALSE) | |
animation::ani.options(interval = 1/30) | |
gganimate(p, "simulation.mp4", title_frame = F, ani.width = 1280, ani.height = 720) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can't attach the gif because of the file size, and can't attach an mp4 because of github's limitations. So I'll just post the final screenshot, and I encourage you to try running this gist on your computer to produce an animation yourself.
The situation seems to destabilize fairly fast towards the last 1% of turns, producing an "economic crisis" with someone getting into an unpayable debt pit, and a handful of players becoming "ultra-wealthy", should the debt ever convert to money. Unfortunately, there isn't enough money in the whole game to account for these numbers.