Final results;
:!for players in MRCDTTTTT MRTTTTTTT MMCTTTTTT MMDTTTTTT; do ./a.out $players; echo; done
Setup:
Mafia:
Normal: 1
Role blocker: 1
Town:
Normal: 6
Doctor: 1
Mafia wins 57.069781% of the time with lynch on Day 1
Mafia wins 65.034042% of the time with no lynch on Day 1
Setup:
Mafia:
Normal: 1
Role blocker: 1
Town:
Normal: 7
Doctor: 0
Mafia wins 70.158730% of the time with lynch on Day 1
Mafia wins 84.375000% of the time with no lynch on Day 1
Setup:
Mafia:
Normal: 2
Role blocker: 0
Town:
Normal: 7
Doctor: 0
Mafia wins 70.158730% of the time with lynch on Day 1
Mafia wins 84.375000% of the time with no lynch on Day 1
Setup:
Mafia:
Normal: 2
Role blocker: 0
Town:
Normal: 6
Doctor: 1
Mafia wins 54.693878% of the time with lynch on Day 1
Mafia wins 61.345564% of the time with no lynch on Day 1
So I guess it is bad for us not to lynch on the first day.Source code:
#include <cstdio>
struct Players
{
enum { NumRoles = 4, NumMafiaRoles = 2, NumTownRoles = 2 };
union
{
struct
{
int mafiaNormal, mafiaRoleBlocker;
int townNormal, townDoctor;
};
struct
{
int mafiaRoles[NumMafiaRoles];
int townRoles[NumTownRoles];
};
int roles[NumRoles];
};
Players()
{
for(int i = 0; i < NumRoles; ++i)
roles[i] = 0;
}
int town() const
{
return townNormal + townDoctor;
}
int mafia() const
{
return mafiaNormal + mafiaRoleBlocker;
}
int total() const
{
return town() + mafia();
}
};
/* 1 = Mafia win; 0 = Town win. */
double checkPossibilities(Players players, bool lynch = true)
{
if(players.town() <= players.mafia())
return 1;
if(players.mafia() == 0)
return 0;
if(lynch)
{
double prob = 0;
for(int role = 0; role < Players::NumRoles; ++role)
{
if(players.roles[role] == 0)
continue;
double weight = (double)players.roles[role] / players.total();
Players newPlayers = players;
--newPlayers.roles[role];
prob += weight * checkPossibilities(newPlayers, false);
}
return prob;
}
/* Handle kills. */
double prob = 0;
for(int townRole = 0; townRole < Players::NumTownRoles; ++townRole)
{
if(players.townRoles[townRole] == 0)
continue;
double weight = (double)players.townRoles[townRole] / players.town();
if(townRole != 1) /* Doctor can't defend himself. */
{
weight *= 1 - (double)players.townDoctor / (players.total() - 1) /* Doctor(s) defends at random. */
* (1 - (double)players.mafiaRoleBlocker / (players.town())); /* Role blocker(s) block Doctor at random. */
}
Players newPlayers = players;
--newPlayers.townRoles[townRole];
prob += weight * checkPossibilities(newPlayers, true);
}
return prob;
}
Players playersFromString(const char *playerString)
{
Players players;
for(/* */; *playerString; ++playerString)
{
switch(*playerString)
{
case 'T':
case 'C':
++players.townNormal;
break;
case 'D':
++players.townDoctor;
break;
case 'M':
++players.mafiaNormal;
break;
case 'R':
++players.mafiaRoleBlocker;
break;
}
}
return players;
}
int main(int argc, char **argv)
{
if(argc != 2)
{
fprintf(stderr, "Argument must be string of players (_M_afia, _R_ole blocker, _T_own, _C_op, _D_octor) e.g. MDTTTTTTT\n");
return 1;
}
Players players = playersFromString(argv[1]);
printf("Setup:\n");
printf("\tMafia:\n");
printf("\t\tNormal: %d\n", players.mafiaNormal);
printf("\t\tRole blocker: %d\n", players.mafiaRoleBlocker);
printf("\tTown:\n");
printf("\t\tNormal: %d\n", players.townNormal);
printf("\t\tDoctor: %d\n", players.townDoctor);
printf("\n");
printf("Mafia wins %f%% of the time with lynch on Day 1\n", checkPossibilities(players, true) * 100);
printf("Mafia wins %f%% of the time with no lynch on Day 1\n", checkPossibilities(players, false) * 100);
return 0;
}