-- returns a "random" number between 1 and n (included) -- feel free to replace this with crypto secure stuff function random(n) if math.random() < 1/n then return n else return random(n-1) end end -- Possible strategies for choosing the first door -- (Applicable for either the prize or the player) function choose_door_1() return 1 end function choose_door_2() return 2 end function choose_door_3() return 3 end function choose_random_door() return random(3) end -- From now on: -- * The car parameter says where the car is. -- * The choice parameter says which door the candidate has picked -- opens the leftmost door the candidate has not chosen function open_leftmost_door(choice) if choice == 1 then return 2 else return 1 end end -- opens the rightmost door the candidate has not chosen function open_rightmost_door(choice) if choice == 3 then return 2 else return 3 end end -- opens one of the doors the candidate has not chosen, at random function open_random_door(choice) if random(2) == 1 then return open_leftmost_door(choice) else return open_rightmost_door(choice) end end -- opens the goat door when there is only one possibility function open_goat(car, choice) if car == choice then error("2 possible choices") else if car ~= 1 and choice ~= 1 then return 1 else if car ~= 2 and choice ~= 2 then return 2 else return 3 end end end end -- Possible strategies for the host, (once the doors have been chosen) -- always opens a goat function the_goat(car, choice) if car == choice then return open_random_door(choice) else return open_goat(car, choice) end end -- opens a door at random function the_random(car, choice) return open_random_door(choice) end -- opens the car (if possible) function the_enemy(car, choice) if car == choice then return open_random_door(choice) else return car end end -- opens the leftmost door function the_communist(car, choice) return open_leftmost_door(choice) end -- opens the rightmost door iff it holds a goat function the_helper(car, choice) if car == choice then return open_rightmost_door(choice) else return open_goat(car, choice) end end -- So we can print like a *real* programmer local printf = function(s, ...) return io.write(s:format(...)) end -- Runs the game once function run_game(player, host) local car = random(3) local choice = player() -- player knows nothing local opened = host(car, choice) -- host knows where the car is if opened == choice then error("the host can't open the door you picked!!!") end printf("Door %d has the car. ", car) printf("You picked door %d. " , choice) printf("Host opened door %d " , opened) if opened == open_leftmost_door(choice) then printf("(left). ") else printf("(right). ") end if opened == car then printf("You lose.\n") else if choice == car then printf("Do not switch.\n") else printf("Please switch.\n") end end end function run_many_games(nb_games, player, host) for i = 1, nb_games do run_game(player, host) end end -- main program. Possible usages: -- Usage: lua monty-hall.lua [[[host] player] nb_games] -- -- Examples: -- lua monty-hall.lua -- lua monty-hall.lua the_goat -- lua monty-hall.lua the_random choose_door_2 -- lua monty-hall.lua the_enemy choose_door_1 50 -- lua monty-hall.lua the_communist -- lua monty-hall.lua the_helper -- -- Possible hosts: -- the_goat (by default) -- the_random -- the_enemy -- the_communist -- the_helper -- -- Possible players: -- choose_random_door (by default) -- choose_door_1 -- choose_door_2 -- choose_door_3 -- -- nb_games is a positive integer. 1000 by default function main() -- default settings local host = "the_goat" local player = "choose_random_door" local nb_games = 1000 if arg[1] ~= nil then host = arg[1] end if arg[2] ~= nil then player = arg[2] end if arg[3] ~= nil then nb_games = arg[3] end run_many_games(tonumber(nb_games), _G[player], _G[host]) end main()