Thanks @bpeter
I incorporated the weights you suggested and now it looks very nice! See the distribution in the histogram and the rolling mean in the figure below. In just 50 rounds, the observed distribution gets very good.
As can be seen in the colored barplot, the probabilities change quite a lot, whether or not this is good/ok may be up for discussion but I think I am satisfied.
(I did a chi-squared test also to see if observed and expected are from the same distribution, but it accepts the null (that they are) always, even in just 10 rounds, therefore not so interesting...)

rm(list=ls())
library(RColorBrewer)
colors <- brewer.pal(11, "Spectral")
outcomes <- c(2,3,4,5,6,7,8,9,10,11,12)
expected <- c(1,2,3,4,5,6,5,4,3,2,1)/36
n = 50
power = 10
memory <- c(0,0,0,0,0,1,0,0,0,0,0)
p_mem <- c()
dice_outcomes = c()
mean = c()
for (i in 1:n){
observed <- memory/sum(memory)
d <- (observed - expected)/expected
w <- (max(d) - d + 0.01)^power
w <- w/sum(w)
prob <- expected * w
prob <- prob / sum(prob)
p_mem <- cbind(p_mem, prob)
a <- cumsum(prob) - runif(1) #generate dice
index <- sum(a < 0) + 1
dice <- outcomes[index]
memory[index] <- memory[index] + 1
dice_outcomes[i] <- dice
mean[i] <- mean(dice_outcomes)
}
exp_memory = expected*n
tab = rbind(exp_memory,memory)
chisq.test(tab)
par(mfrow = c(2,2))
hist(dice_outcomes, breaks = c(1,2,3,4,5,6,7,8,9,10,11,12))
plot(mean, type = "l", xlab = "n")
abline(h=7, col = "red")
barplot(p_mem, col = colors)
And with some small changes if one wants to use the code in an actual game, simulating one dice at the time:
#weighted dice for Settlers of Catan or other board games where "the Gambler's Fallacy" is wanted
#since it otherwise takes to long time to reach true probability distribution.
#INSTRUCTIONS: Run first the whole program. Then run from row 18 to end to get dice 2...n
rm(list=ls())
library(RColorBrewer)
colors <- brewer.pal(11, "Spectral")
outcomes <- c(2,3,4,5,6,7,8,9,10,11,12)
expected <- c(1,2,3,4,5,6,5,4,3,2,1)/36
power = 10
memory <- c(0,0,0,0,0,1,0,0,0,0,0)
p_mem <- c()
dice_outcomes = c()
mean = c()
i = 1
##########################################################
#Dice nr 2...n
observed <- memory/sum(memory)
d <- (observed - expected)/expected
w <- (max(d) - d + 0.01)^power
w <- w/sum(w)
prob <- expected * w
prob <- prob / sum(prob)
p_mem <- cbind(p_mem, prob)
#generate dice
a <- cumsum(prob) - runif(1)
index <- sum(a < 0) + 1
dice <- outcomes[index]
memory[index] <- memory[index] + 1
dice_outcomes[i] <- dice
mean[i] <- mean(dice_outcomes)
i <- i+1
#plots
par(mfrow = c(2,2))
hist(dice_outcomes, breaks = c(1,2,3,4,5,6,7,8,9,10,11,12))
plot(mean, type = "l", xlab = "n")
abline(h=7, col = "red")
barplot(p_mem, col = colors)
plot(c(0, 1), c(0, 1), ann = F, bty = 'n', type = 'n', xaxt = 'n', yaxt = 'n')
if (dice == 7){a = "red"}else{a = "blue"}
text(x = 0.5, y = 0.5, paste(dice), cex = 10, col = a)
The problem with draw with out replacement is that if I count the outcomes of X along the way, in the end I can know what is left, or that "all X = 8 has happened". Also, games can be of different lengths (say 30 up to 100).
Instead I want to keep some randomness, but avoid the chance of X = [5,5,5] to happen and if X = 3 has not happened in 20 rounds, increase that probability. Therefore the "probability weights" should shift automatically depending on the history of outcomes so that the true probability is reached as fast as possible.
– SalamEkshi Sep 07 '16 at 15:23