Commit 32523b47 authored by Adam Štěpánek's avatar Adam Štěpánek
Browse files

Add ParanoidStrategy

parent ba266a7d
Loading
Loading
Loading
Loading
+303 −0
Original line number Diff line number Diff line
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Adam.SecretHitler;

namespace Adam.SecretHitler
{
    public class ParanoidStrategy : IStrategy
    {
        public const double BetrayalTrustThreshold = -50.0;
        public const int PretendFascistPolicyThreshold = 2;
        public const int PretendLiberalPolicyThreshold = 3;
        public const double CooperationTrustReward = 50.0;
        public const double BetrayalTrustPenalty = -100.0;
        public const double TrustRate = 2.0;
        public const double DefaultTrust = 100.0;
        public const double DefaultParanoia = 10.0;
        public const double AllyTrust = 10000.0;
        public const double ParanoiaRate = 1.5;

        private readonly Random random;
        private double[] trust = Array.Empty<double>();
        private Player self = Player.None;
        private Player hitler;
        private Role role = Role.None;
        private ImmutableArray<Role> roles = ImmutableArray.Create<Role>();
        private int fascistPolicyCount;
        private int liberalPolicyCount;
        private double paranoia = DefaultParanoia;
        private Party ally;
        private Party enemy;

        public ParanoidStrategy(Random random)
        {
            this.random = random;
        }

        public void SetUp(PublicState state, Player self, Role role, Player hitler, ImmutableArray<Player> fascists)
        {
            this.self = self;
            this.role = role;
            this.hitler = hitler;
            trust = new double[state.PlayerCount];
            Array.Fill(trust, DefaultTrust);
            if (role == Role.Fascist)
            {
                var builder = ImmutableArray.CreateBuilder<Role>(state.PlayerCount);
                builder.AddRange(Enumerable.Repeat(Role.Liberal, state.PlayerCount));
                builder[hitler] = Role.Hitler;
                trust[hitler] = AllyTrust;
                foreach (var fascist in fascists)
                {
                    trust[fascist] = AllyTrust;
                    builder[fascist] = Role.Fascist;
                }
                roles = builder.ToImmutable();
            }

            if (role == Role.Liberal)
            {
                ally = Party.Liberal;
                enemy = Party.Fascist;
            }
            else
            {
                ally = Party.Fascist;
                enemy = Party.Liberal;
            }
        }

        public Player CallSpecialElection(ImmutableArray<Player> eligible)
        {
            return GetMostTrusted(eligible);
        }

        public Party ConcludeInvestigation(Player investigated, Party partyMembership)
        {
            switch (role)
            {
                case Role.Hitler:
                case Role.Liberal:
                    trust[investigated] = partyMembership == ally
                        ? AllyTrust
                        : -AllyTrust;

                    return partyMembership;
                case Role.Fascist:
                    if (partyMembership == Party.Fascist || trust[investigated] > BetrayalTrustThreshold)
                    {
                        return Party.Liberal;
                    }

                    return Party.Fascist;
                default:
                    throw new InvalidOperationException("Invalid Role encountered.");
            }
        }

        public Party DiscardAsPresident(
            ImmutableArray<Party> hand,
            out ImmutableArray<Party> presidentComment,
            out bool wouldVeto)
        {
            return Discard(hand, out presidentComment, out wouldVeto);
        }

        public Party DiscardAsChancellor(ImmutableArray<Party> hand, out ImmutableArray<Party> chancellorComment, out bool wantsVeto)
        {
            return Discard(hand, out chancellorComment, out wantsVeto);
        }

        public Player ExecutePlayer(ImmutableArray<Player> executable)
        {
            return GetLeastTrustedNonAlly(executable);
        }

        public Player InvestigateLoyalty(ImmutableArray<Player> investigable)
        {
            return GetLeastTrustedNonAlly(investigable);
        }

        public Player NominateChancellor(ImmutableArray<Player> eligible)
        {
            if (role == Role.Fascist && fascistPolicyCount >= 3 && eligible.Contains(hitler))
            {
                return hitler;
            }

            return GetMostTrustedAlly(eligible);
        }

        public bool Vote(Player presidentElect, Player chancellorElect)
        {
            return presidentElect == self || chancellorElect == self
                || (trust[presidentElect] > paranoia && trust[chancellorElect] >= paranoia);
        }

        public void OnRound(PublicState state)
        {
            fascistPolicyCount = state.FascistPolicyCount;
            liberalPolicyCount = state.LiberalPolicyCount;
            if (!state.LastRound.IsElectionSuccessful)
            {
                return;
            }

            var roundParanoiaRate = 1.0;

            var allyPolicyCount = role == Role.Liberal
                ? state.LiberalPolicyCount
                : state.FascistPolicyCount;
            var enemyPolicyCount = role == Role.Liberal
                ? state.FascistPolicyCount
                : state.LiberalPolicyCount;
            var governmentTrustChange = state.LastRound.ElectedPolicy == ally
                ? allyPolicyCount * CooperationTrustReward
                : enemyPolicyCount * BetrayalTrustPenalty;
            trust[state.LastRound.PresidentElect] += governmentTrustChange;
            var isMyGovernment = state.LastRound.PresidentElect == self || state.LastRound.ChancellorElect == self;
            var storyChecksOut = SH.DoesStoryCheckOut(
                state.LastRound.ElectedPolicy,
                state.LastRound.PresidentComment,
                state.LastRound.ChancellorComment);
            if (isMyGovernment && storyChecksOut)
            {
                governmentTrustChange += CooperationTrustReward;
            }

            if (!storyChecksOut)
            {
                roundParanoiaRate += ParanoiaRate;
                governmentTrustChange += BetrayalTrustPenalty;
            }

            trust[state.LastRound.ChancellorElect] += governmentTrustChange;

            if (state.LastRound.Power == PresidentialPower.InvestigateLoyalty
                && trust[state.LastRound.PresidentElect] > paranoia)
            {
                if (state.LastRound.InvestigateLoyaltyComment == ally)
                {
                    trust[state.LastRound.InvestigatedPlayer] += CooperationTrustReward;
                }
                else if (state.LastRound.InvestigateLoyaltyComment == enemy)
                {
                    trust[state.LastRound.InvestigatedPlayer] += BetrayalTrustPenalty;
                }
                else
                {
                    trust[state.LastRound.PresidentElect] += BetrayalTrustPenalty;
                }
            }

            if (state.LastRound.Power == PresidentialPower.Execution)
            {
                roundParanoiaRate += ParanoiaRate;
                trust[state.LastRound.PresidentElect] += trust[state.LastRound.ExecutedPlayer] < paranoia
                    && trust[state.LastRound.PresidentElect] > paranoia
                        ? CooperationTrustReward
                        : BetrayalTrustPenalty;
            }

            paranoia *= roundParanoiaRate;
        }

        private Party Discard(
            ImmutableArray<Party> hand,
            out ImmutableArray<Party> comment,
            out bool veto)
        {
            if (role == Role.Liberal || (fascistPolicyCount < PretendFascistPolicyThreshold
                && liberalPolicyCount < PretendLiberalPolicyThreshold))
            {
                comment = hand;
                if (hand.Contains(enemy))
                {
                    veto = false;
                    return enemy;
                }

                veto = true;
                return ally;
            }

            // only fascists and hitler from this point on
            if (hand.Contains(Party.Fascist))
            {
                comment = hand.Length == 3 ? SH.FascistThree : SH.FascistTwo;
                veto = false;
                return hand.Contains(Party.Liberal) ? Party.Liberal : Party.Fascist;
            }

            comment = hand;
            veto = true;
            return Party.Liberal;
        }

        private ImmutableArray<Player> GetMostTrustedGroup(IEnumerable<Player> from)
        {
            return from.GroupBy(p => trust[p])
                .OrderByDescending(g => g.Key)
                .First()
                .ToImmutableArray();
        }

        private Player GetMostTrusted(IEnumerable<Player> from)
        {
            var group = GetMostTrustedGroup(from);
            return group[random.Next(0, group.Length)];
        }

        private Player GetMostTrustedAlly(IEnumerable<Player> from)
        {
            if (role != Role.Fascist)
            {
                return GetMostTrusted(from);
            }

            var results = from.OrderByDescending(p => trust[p])
                .Where(p => p != self && (roles[p] == Role.Hitler || roles[p] == Role.Fascist))
                .ToImmutableArray();
            if (results.Length == 0)
            {
                return GetMostTrusted(from);
            }

            return results[0];
        }

        private ImmutableArray<Player> GetLeastTrustedGroup(IEnumerable<Player> from)
        {
            return from.GroupBy(p => trust[p])
                .OrderBy(g => g.Key)
                .First()
                .ToImmutableArray();
        }

        private Player GetLeastTrusted(IEnumerable<Player> from)
        {
            var group = GetLeastTrustedGroup(from);
            return group[random.Next(0, group.Length)];
        }

        private Player GetLeastTrustedNonAlly(IEnumerable<Player> from)
        {
            if (role != Role.Fascist)
            {
                return GetLeastTrusted(from);
            }

            var results = from.OrderBy(p => trust[p])
                .Where(p => p != self && roles[p] != Role.Hitler && roles[p] != Role.Fascist)
                .ToImmutableArray();
            if (results.Length == 0)
            {
                return GetLeastTrusted(from);
            }

            return results[0];
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ namespace Adam.SecretHitler
            Simple,
            Cfr,
            CounterSimple,
            Smart
            Paranoid
        }

        public static int Main(string[] args)
@@ -129,7 +129,7 @@ namespace Adam.SecretHitler
                    AvailableStrategy.Simple => new SimpleStrategy(GlobalRandom),
                    AvailableStrategy.Cfr => new CfrTrainedStrategy(GlobalRandom, cfrInput),
                    AvailableStrategy.CounterSimple => new CounterSimpleStrategy(GlobalRandom),
                    AvailableStrategy.Smart => new SmartStrategy(GlobalRandom),
                    AvailableStrategy.Paranoid => new ParanoidStrategy(GlobalRandom),
                    _ => new NotImplementedStrategy()
                };

src/SmartStrategy.Fascist.cs

deleted100644 → 0
+0 −271
Original line number Diff line number Diff line
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace Adam.SecretHitler
{
    public partial class SmartStrategy : StrategyWrapper
    {
        public class Fascist : IStrategy
        {
            public const int DefaultAllyTrust = 1000;
            public const int InvestigationBetrayalThreshold = 100;
            public const int VoteOnGovernmentThreshold = 10;
            public const int PolicyBetrayalThreshold = -50;
            public const int StoryChecksOutBoost = 10;
            public const int FascistPolicyEnactedBoost = 10;
            public const int BSStoryPenalty = -20;
            public const int LiberalPolicyEnactedPenalty = -10;
            public const int FascistPolicyCountFactor = 2;
            public const int LiberalPolicyCountFactor = 1;
            public const int InvestigationBetrayalPenalty = -50;
            public const int InvestigationBetrayalBoost = 50;
            public const int ExecutionPenalty = -100;
            public const int ExecutionBoost = 100;
            public const int FakeLiberalThreshold = 2;

            private readonly Random random = new Random();
            private int[] trust = Array.Empty<int>();
            private Player self = Player.None;
            private Player hitler;
            private ImmutableArray<Role> roles;
            private Player presidentElect;
            private Player chancellorElect;
            private int fascistPolicyCount;

            public Fascist(Random random)
            {
                this.random = random;
            }

            public void SetUp(PublicState state, Player self, Role role, Player hitler, ImmutableArray<Player> fascists)
            {
                this.self = self;
                this.hitler = hitler;
                var builder = ImmutableArray.CreateBuilder<Role>(state.PlayerCount);
                builder.AddRange(Enumerable.Repeat(Role.Liberal, state.PlayerCount));
                builder[hitler] = Role.Hitler;
                trust = new int[state.PlayerCount];
                trust[hitler] = DefaultAllyTrust;
                foreach (var fascist in fascists)
                {
                    trust[fascist] = DefaultAllyTrust;
                    builder[fascist] = Role.Fascist;
                }
                roles = builder.ToImmutable();
            }

            public Player CallSpecialElection(ImmutableArray<Player> eligible)
                => GetMostTrusted(eligible);

            public Party ConcludeInvestigation(Player investigated, Party partyMembership)
            {
                if (partyMembership == Party.Fascist)
                {
                    return Party.Liberal;
                }

                if (trust[investigated] < InvestigationBetrayalThreshold)
                {
                    return Party.Fascist;
                }

                return Party.Liberal;
            }

            public Party DiscardAsChancellor(
                ImmutableArray<Party> hand,
                out ImmutableArray<Party> chancellorComment,
                out bool wantsVeto)
            {
                if (hand.Contains(Party.Fascist)
                    && (trust[presidentElect] <= PolicyBetrayalThreshold || roles[presidentElect] == Role.Fascist))
                {
                    chancellorComment = SH.FascistTwo;
                    wantsVeto = false;
                    return hand.Contains(Party.Liberal) ? Party.Liberal : Party.Fascist;
                }

                if (hand.Contains(Party.Liberal))
                {
                    chancellorComment = hand.Remove(Party.Liberal).Add(Party.Fascist);
                    wantsVeto = false;
                    return Party.Liberal;
                }

                chancellorComment = hand;
                wantsVeto = true;
                return Party.Fascist;
            }

            public Party DiscardAsPresident(
                ImmutableArray<Party> hand,
                out ImmutableArray<Party> presidentComment,
                out bool wouldVeto)
            {
                if ((roles[chancellorElect] == Role.Fascist || roles[chancellorElect] == Role.Hitler)
                    && hand.Contains(Party.Fascist)
                    && fascistPolicyCount <= FakeLiberalThreshold)
                {
                    presidentComment = SH.FascistThree;
                    wouldVeto = false;
                    return hand.Contains(Party.Liberal) ? Party.Liberal : Party.Fascist;
                }

                if (hand.Contains(Party.Liberal))
                {
                    presidentComment = hand.Remove(Party.Liberal).Add(Party.Fascist);
                    wouldVeto = false;
                    return Party.Liberal;
                }

                presidentComment = hand;
                wouldVeto = true;
                return Party.Fascist;
            }

            public Player ExecutePlayer(ImmutableArray<Player> executable)
                => GetLeastTrustedNonAlly(executable);

            public Player InvestigateLoyalty(ImmutableArray<Player> investigable)
                => GetLeastTrustedNonAlly(investigable);

            public Player NominateChancellor(ImmutableArray<Player> eligible)
            {
                if (fascistPolicyCount >= 3 && eligible.Contains(hitler))
                {
                    return hitler;
                }

                return GetMostTrustedAlly(eligible);
            }

            public void OnRound(PublicState state)
            {
                fascistPolicyCount = state.FascistPolicyCount;
                if (!state.LastRound.IsElectionSuccessful)
                {
                    return;
                }

                if (state.LastRound.ElectedPolicy == Party.Fascist)
                {
                    trust[state.LastRound.PresidentElect]
                        += FascistPolicyCountFactor * state.FascistPolicyCount * FascistPolicyEnactedBoost;
                    trust[state.LastRound.ChancellorElect]
                        += FascistPolicyCountFactor * state.FascistPolicyCount * FascistPolicyEnactedBoost;
                }
                else
                {
                    trust[state.LastRound.PresidentElect]
                        += LiberalPolicyCountFactor * state.LiberalPolicyCount * LiberalPolicyEnactedPenalty;
                    trust[state.LastRound.ChancellorElect]
                        += LiberalPolicyCountFactor * state.LiberalPolicyCount * LiberalPolicyEnactedPenalty;
                }

                if (SH.DoesStoryCheckOut(
                    state.LastRound.ElectedPolicy,
                    state.LastRound.PresidentComment,
                    state.LastRound.ChancellorComment))
                {
                    trust[state.LastRound.PresidentElect] += StoryChecksOutBoost;
                    trust[state.LastRound.ChancellorElect] += StoryChecksOutBoost;
                }
                else
                {
                    trust[state.LastRound.PresidentElect] += BSStoryPenalty;
                    trust[state.LastRound.ChancellorElect] += BSStoryPenalty;
                }

                if (state.LastRound.Power == PresidentialPower.InvestigateLoyalty)
                {
                    var realRole = roles[state.LastRound.InvestigatedPlayer];
                    if (state.LastRound.InvestigateLoyaltyComment == Party.Fascist)
                    {
                        trust[state.LastRound.PresidentElect] += realRole == Role.Hitler || realRole == Role.Fascist
                            ? InvestigationBetrayalPenalty
                            : InvestigationBetrayalBoost;
                    }
                    else if (state.LastRound.InvestigateLoyaltyComment == Party.Liberal)
                    {
                        trust[state.LastRound.PresidentElect] += realRole == Role.Hitler || realRole == Role.Fascist
                            ? InvestigationBetrayalBoost
                            : InvestigationBetrayalPenalty;
                    }
                }

                if (state.LastRound.Power == PresidentialPower.Execution)
                {
                    var role = roles[state.LastRound.ExecutedPlayer];
                    trust[state.LastRound.PresidentElect] += role == Role.Fascist
                        ? ExecutionPenalty
                        : ExecutionBoost;
                }
            }

            public bool Vote(Player presidentElect, Player chancellorElect)
            {
                this.presidentElect = presidentElect;
                this.chancellorElect = chancellorElect;

                return presidentElect == self || chancellorElect == self
                    || trust[presidentElect] + trust[chancellorElect] >= VoteOnGovernmentThreshold;
            }

            private ImmutableArray<Player> GetMostTrustedGroup(IEnumerable<Player> from)
            {
                return from.GroupBy(p => trust[p])
                    .OrderByDescending(g => g.Key)
                    .First()
                    .ToImmutableArray();
            }

            private Player GetMostTrusted(IEnumerable<Player> from)
            {
                var group = GetMostTrustedGroup(from);
                return group[random.Next(0, group.Length)];
            }

            private Player GetMostTrustedAlly(IEnumerable<Player> from)
            {
                var results = from.OrderByDescending(p => trust[p])
                    .Where(p => p != self && (roles[p] == Role.Hitler || roles[p] == Role.Fascist))
                    .ToImmutableArray();
                if (results.Length == 0)
                {
                    return GetMostTrusted(from);
                }

                return results[0];
            }

            private ImmutableArray<Player> GetLeastTrustedGroup(IEnumerable<Player> from)
            {
                return from.GroupBy(p => trust[p])
                    .OrderBy(g => g.Key)
                    .First()
                    .ToImmutableArray();
            }

            private Player GetLeastTrusted(IEnumerable<Player> from)
            {
                var group = GetLeastTrustedGroup(from);
                return group[random.Next(0, group.Length)];
            }

            private Player GetLeastTrustedNonAlly(IEnumerable<Player> from)
            {
                var results = from.OrderBy(p => trust[p])
                    .Where(p => p != self && roles[p] != Role.Hitler && roles[p] != Role.Fascist)
                    .ToImmutableArray();
                if (results.Length == 0)
                {
                    return GetLeastTrusted(from);
                }

                return results[0];
            }
        }
    }
}

src/SmartStrategy.Hitler.cs

deleted100644 → 0
+0 −61
Original line number Diff line number Diff line
using System;
using System.Collections.Immutable;

namespace Adam.SecretHitler
{
    public partial class SmartStrategy : StrategyWrapper
    {
        public class Hitler : IStrategy
        {
            public Player CallSpecialElection(ImmutableArray<Player> eligible)
            {
                throw new NotImplementedException();
            }

            public Party ConcludeInvestigation(Player investigated, Party partyMembership)
            {
                throw new NotImplementedException();
            }

            public Party DiscardAsChancellor(ImmutableArray<Party> hand, out ImmutableArray<Party> chancellorComment, out bool wantsVeto)
            {
                throw new NotImplementedException();
            }

            public Party DiscardAsPresident(ImmutableArray<Party> hand, out ImmutableArray<Party> presidentComment, out bool wouldVeto)
            {
                throw new NotImplementedException();
            }

            public Player ExecutePlayer(ImmutableArray<Player> executable)
            {
                throw new NotImplementedException();
            }

            public Player InvestigateLoyalty(ImmutableArray<Player> investigable)
            {
                throw new NotImplementedException();
            }

            public Player NominateChancellor(ImmutableArray<Player> eligible)
            {
                throw new NotImplementedException();
            }

            public void OnRound(PublicState state)
            {
                throw new NotImplementedException();
            }

            public void SetUp(PublicState state, Player self, Role role, Player hitler, ImmutableArray<Player> fascists)
            {
                throw new NotImplementedException();
            }

            public bool Vote(Player presidentElect, Player chancellorElect)
            {
                throw new NotImplementedException();
            }
        }
    }
}

src/SmartStrategy.Liberal.cs

deleted100644 → 0
+0 −61

File deleted.

Preview size limit exceeded, changes collapsed.

Loading