├── .gitignore ├── Monopoly.sln ├── Monopoly ├── Analytics.cs ├── Monopoly.csproj ├── Monopoly │ ├── Board.cs │ ├── NeuralPlayer.cs │ └── Player.cs ├── NetworkAdapter.cs ├── NeuroEvolution │ ├── Crossover.cs │ ├── Genotype.cs │ ├── Mutation.cs │ ├── NetworkFactory.cs │ ├── Phenotype.cs │ ├── Population.cs │ └── Program.cs ├── Program.cs ├── RNG.cs └── Tournament.cs ├── README.txt ├── monopoly_population 162.txt ├── monopoly_population champions.txt └── monopoly_population.txt /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /Monopoly.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31624.102 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monopoly", "Monopoly\Monopoly.csproj", "{4FAA85B2-9907-4F4F-9BFB-0DE233C176D5}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {4FAA85B2-9907-4F4F-9BFB-0DE233C176D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {4FAA85B2-9907-4F4F-9BFB-0DE233C176D5}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {4FAA85B2-9907-4F4F-9BFB-0DE233C176D5}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {4FAA85B2-9907-4F4F-9BFB-0DE233C176D5}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {556F7D50-7839-4CB0-9C07-3DFD93EC8419} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Monopoly/Analytics.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Monopoly 8 | { 9 | public class Analytics 10 | { 11 | public static Analytics instance = null; 12 | 13 | public int[] bids; 14 | public int[] money; 15 | public float[] average; 16 | public float[] price; 17 | 18 | public int[] trades; 19 | public int[] exchanges; 20 | 21 | public int[] wins; 22 | public int max = 0; 23 | public int min = 0; 24 | public float[] ratio; 25 | 26 | public Analytics() 27 | { 28 | bids = new int[40]; 29 | money = new int[40]; 30 | average = new float[40]; 31 | price = new float[40]; 32 | 33 | trades = new int[40]; 34 | exchanges = new int[40]; 35 | 36 | wins = new int[40]; 37 | ratio = new float[40]; 38 | } 39 | 40 | public void MakeBid(int index, int bid) 41 | { 42 | bids[index]++; 43 | money[index] += bid; 44 | average[index] = money[index] / bids[index]; 45 | price[index] = average[index] / MONOPOLY.Board.COSTS[index]; 46 | } 47 | 48 | public void MadeTrade(int index) 49 | { 50 | trades[index]++; 51 | } 52 | 53 | public void MarkWin(int index) 54 | { 55 | wins[index]++; 56 | 57 | if (max < wins[index]) 58 | { 59 | max = wins[index]; 60 | } 61 | 62 | int tempMin = int.MaxValue; 63 | 64 | for (int i = 0; i < 40; i++) 65 | { 66 | if (wins[i] != 0 && wins[i] < tempMin) 67 | { 68 | tempMin = wins[i]; 69 | } 70 | } 71 | 72 | ratio[index] = (float)(wins[index] - tempMin) / (float)(max - tempMin); 73 | } 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Monopoly/Monopoly.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | bin\Release\ 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Monopoly/Monopoly/Board.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MONOPOLY 8 | { 9 | public class Board 10 | { 11 | public enum EOutcome 12 | { 13 | ONGOING, 14 | DRAW, 15 | WIN1, 16 | WIN2, 17 | WIN3, 18 | WIN4, 19 | } 20 | 21 | public enum EMode 22 | { 23 | ROLL, 24 | } 25 | 26 | public enum ETile 27 | { 28 | NONE, 29 | PROPERTY, 30 | TRAIN, 31 | UTILITY, 32 | CHANCE, 33 | CHEST, 34 | TAX, 35 | JAIL, 36 | } 37 | 38 | public enum ECard 39 | { 40 | ADVANCE, 41 | RAILROAD2, 42 | UTILITY10, 43 | REWARD, 44 | CARD, 45 | BACK3, 46 | JAIL, 47 | REPAIRS, 48 | STREET, 49 | FINE, 50 | CHAIRMAN, 51 | BIRTHDAY, 52 | } 53 | 54 | public class CardEntry 55 | { 56 | public ECard card; 57 | public int val; 58 | 59 | public CardEntry(ECard c, int v) 60 | { 61 | card = c; 62 | val = v; 63 | } 64 | } 65 | 66 | //constants 67 | //-------------------- 68 | public static int PLAYER_COUNT = 4; 69 | 70 | public static int BANK_INDEX = -1; 71 | 72 | public static int BOARD_LENGTH = 40; 73 | public static int STALEMATE_TURN = 300; 74 | 75 | public static int GO_BONUS = 200; 76 | public static int GO_LANDING_BONUS = 200; 77 | 78 | public static int JAIL_INDEX = 10; 79 | public static int JAIL_PENALTY = 50; 80 | 81 | public static float MORTGAGE_INTEREST = 1.1f; 82 | 83 | //penalties for landing on a property (all circumstances) 84 | public static int[,] PROPERTY_PENALTIES = new int[16, 6] 85 | {{2, 10, 30, 90, 160, 250 }, 86 | { 4, 20, 60, 180, 320, 450 }, 87 | { 6, 30, 90, 270, 400, 550 }, 88 | { 8, 40, 100, 300, 450, 600 }, 89 | { 10, 50, 150, 450, 625, 750 }, 90 | { 12, 60, 180, 500, 700, 900 }, 91 | { 14, 70, 200, 550, 750, 950 }, 92 | { 16, 80, 220, 600, 800, 1000 }, 93 | { 18, 90, 250, 700, 875, 1050 }, 94 | { 20, 100, 300, 750, 925, 1100 }, 95 | { 22, 110, 330, 800, 975, 1150 }, 96 | { 22, 120, 360, 850, 1025, 1200 }, 97 | { 26, 130, 390, 900, 1100, 1275 }, 98 | { 28, 150, 450, 1000, 1200, 1400 }, 99 | { 35, 175, 500, 1100, 1300, 1500 }, 100 | { 50, 200, 600, 1400, 1700, 2000 }}; 101 | 102 | //penalities for landing on utilities (needs to be multiplied by roll) 103 | public static int[] UTILITY_POSIIONS = new int[2] { 12, 28 }; 104 | public static int[] UTILITY_PENALTIES = new int[2] { 4, 10 }; 105 | 106 | //penalties for landing on trains 107 | public static int[] TRAIN_POSITIONS = new int[4] { 5, 15, 25, 35 }; 108 | public static int[] TRAIN_PENALTIES = new int[4] { 25, 50, 100, 200 }; 109 | 110 | public static ETile[] TYPES = new ETile[40] 111 | {ETile.NONE, ETile.PROPERTY, ETile.CHEST, ETile.PROPERTY, ETile.TAX, ETile.TRAIN, ETile.PROPERTY, ETile.CHANCE, ETile.PROPERTY, ETile.PROPERTY, ETile.NONE, 112 | ETile.PROPERTY, ETile.UTILITY, ETile.PROPERTY, ETile.PROPERTY, ETile.TRAIN, ETile.PROPERTY, ETile.CHEST, ETile.PROPERTY, ETile.PROPERTY, ETile.NONE, 113 | ETile.PROPERTY, ETile.CHANCE, ETile.PROPERTY, ETile.PROPERTY, ETile.TRAIN, ETile.PROPERTY, ETile.PROPERTY, ETile.UTILITY, ETile.PROPERTY, ETile.JAIL, 114 | ETile.PROPERTY, ETile.PROPERTY, ETile.CHEST, ETile.PROPERTY, ETile.TRAIN, ETile.CHANCE, ETile.PROPERTY, ETile.TAX, ETile.PROPERTY}; 115 | 116 | public static int[] COSTS = new int[40] { 0, 60, 0, 60, 200, 200, 100, 0, 100, 120, 0, 140, 150, 140, 160, 200, 180, 0, 180, 200, 0, 220, 0, 220, 240, 200, 260, 260, 150, 280, 0, 300, 300, 0, 320, 200, 0, 250, 100, 400 }; 117 | public static int[] BUILD = new int[16] { 50, 50, 50, 50, 100, 100, 100, 100, 150, 150, 150, 150, 200, 200, 200, 200 }; 118 | 119 | public static int[,] SETS = new int[8, 3] 120 | {{1, 3, -1}, 121 | { 6, 8, 9}, 122 | { 11, 13, 14 }, 123 | { 16, 18, 19 }, 124 | { 21, 23, 24}, 125 | { 26, 27, 29 }, 126 | { 31, 32, 34 }, 127 | { 37, 39, -1 }}; 128 | //-------------------- 129 | 130 | //board states 131 | //-------------------- 132 | public bool[] mortgaged = new bool[40] { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }; 133 | 134 | public int[] owners = new int[40] { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; 135 | 136 | public int[] property = new int[40] { -1, 0, -1, 1, -1, -1, 2, -1, 2, 3, -1, 4, -1, 4, 5, -1, 6, -1, 6, 7, -1, 8, -1, 8, 9, -1, 10, 10, -1, 11, -1, 12, 12, -1, 13, -1, -1, 14, -1, 15 }; 137 | 138 | public int[] houses = new int[40] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 139 | 140 | public int[] original = new int[40] { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; 141 | //-------------------- 142 | 143 | public EMode mode = EMode.ROLL; 144 | 145 | public NeuralPlayer[] players; 146 | public NetworkAdapter adapter; 147 | public RNG random; 148 | 149 | public int turn = 0; 150 | public int count = 0; 151 | public int remaining = 0; 152 | 153 | public int last_roll = 0; 154 | 155 | //card stacks 156 | //-------------------- 157 | public List chance; 158 | public List chest; 159 | //-------------------- 160 | 161 | public Board(NetworkAdapter _adapter) 162 | { 163 | players = new NeuralPlayer[PLAYER_COUNT]; 164 | random = new RNG(); 165 | 166 | adapter = _adapter; 167 | 168 | for (int i = 0; i < PLAYER_COUNT; i++) 169 | { 170 | players[i] = new NeuralPlayer(); 171 | 172 | adapter.SetPosition(i, players[i].position); 173 | adapter.SetMoney(i, players[i].funds); 174 | } 175 | 176 | remaining = PLAYER_COUNT; 177 | 178 | chance = new List(); 179 | chest = new List(); 180 | 181 | //chance 182 | //-------------------- 183 | chance.Add(new CardEntry(ECard.ADVANCE, 39)); 184 | chance.Add(new CardEntry(ECard.ADVANCE, 0)); 185 | chance.Add(new CardEntry(ECard.ADVANCE, 24)); 186 | chance.Add(new CardEntry(ECard.ADVANCE, 11)); 187 | chance.Add(new CardEntry(ECard.RAILROAD2, 0)); 188 | chance.Add(new CardEntry(ECard.RAILROAD2, 0)); 189 | chance.Add(new CardEntry(ECard.UTILITY10, 0)); 190 | chance.Add(new CardEntry(ECard.REWARD, 50)); 191 | chance.Add(new CardEntry(ECard.CARD, 0)); 192 | chance.Add(new CardEntry(ECard.BACK3, 0)); 193 | chance.Add(new CardEntry(ECard.JAIL, 0)); 194 | chance.Add(new CardEntry(ECard.REPAIRS, 0)); 195 | chance.Add(new CardEntry(ECard.FINE, 15)); 196 | chance.Add(new CardEntry(ECard.ADVANCE, 5)); 197 | chance.Add(new CardEntry(ECard.CHAIRMAN, 0)); 198 | chance.Add(new CardEntry(ECard.REWARD, 150)); 199 | chance = random.Shuffle(chance); 200 | //-------------------- 201 | 202 | //chest 203 | //-------------------- 204 | chest.Add(new CardEntry(ECard.ADVANCE, 0)); 205 | chest.Add(new CardEntry(ECard.REWARD, 200)); 206 | chest.Add(new CardEntry(ECard.FINE, 50)); 207 | chest.Add(new CardEntry(ECard.REWARD, 50)); 208 | chest.Add(new CardEntry(ECard.CARD, 0)); 209 | chest.Add(new CardEntry(ECard.JAIL, 0)); 210 | chest.Add(new CardEntry(ECard.REWARD, 100)); 211 | chest.Add(new CardEntry(ECard.REWARD, 20)); 212 | chest.Add(new CardEntry(ECard.BIRTHDAY, 0)); 213 | chest.Add(new CardEntry(ECard.REWARD, 100)); 214 | chest.Add(new CardEntry(ECard.FINE, 100)); 215 | chest.Add(new CardEntry(ECard.FINE, 50)); 216 | chest.Add(new CardEntry(ECard.FINE, 25)); 217 | chest.Add(new CardEntry(ECard.STREET, 0)); 218 | chest.Add(new CardEntry(ECard.REWARD, 10)); 219 | chest.Add(new CardEntry(ECard.REWARD, 100)); 220 | chest = random.Shuffle(chest); 221 | //-------------------- 222 | } 223 | 224 | public EOutcome Step() 225 | { 226 | switch (mode) 227 | { 228 | case EMode.ROLL: return Roll(); 229 | } 230 | 231 | return EOutcome.ONGOING; 232 | } 233 | 234 | public EOutcome Roll() 235 | { 236 | BeforeTurn(); 237 | 238 | int d1 = random.gen.Next(1, 7); 239 | int d2 = random.gen.Next(1, 7); 240 | 241 | last_roll = d1 + d2; 242 | 243 | bool isDouble = d1 == d2; 244 | bool doubleInJail = false; 245 | 246 | if (players[turn].state == Player.EState.JAIL) 247 | { 248 | adapter.SetTurn(turn); 249 | Player.EJailDecision decision = players[turn].DecideJail(); 250 | 251 | if (decision == Player.EJailDecision.ROLL) 252 | { 253 | //regular jail state 254 | if (isDouble) 255 | { 256 | players[turn].jail = 0; 257 | players[turn].state = Player.EState.NORMAL; 258 | 259 | adapter.SetJail(turn, 0); 260 | 261 | doubleInJail = true; 262 | } 263 | else 264 | { 265 | players[turn].jail++; 266 | 267 | if (players[turn].jail >= 3) 268 | { 269 | Payment(turn, JAIL_PENALTY); 270 | 271 | players[turn].jail = 0; 272 | players[turn].state = Player.EState.NORMAL; 273 | 274 | adapter.SetJail(turn, 0); 275 | } 276 | } 277 | } 278 | else if (decision == Player.EJailDecision.PAY) 279 | { 280 | Payment(turn, JAIL_PENALTY); 281 | 282 | players[turn].jail = 0; 283 | players[turn].state = Player.EState.NORMAL; 284 | 285 | adapter.SetJail(turn, 0); 286 | } 287 | else if (decision == Player.EJailDecision.CARD) 288 | { 289 | if (players[turn].card > 0) 290 | { 291 | players[turn].card--; 292 | players[turn].jail = 0; 293 | players[turn].state = Player.EState.NORMAL; 294 | 295 | adapter.SetJail(turn, 0); 296 | adapter.SetCard(turn, players[turn].card > 0 ? 1 : 0); 297 | } 298 | else 299 | { 300 | //run regular jail state 301 | if (isDouble) 302 | { 303 | players[turn].jail = 0; 304 | players[turn].state = Player.EState.NORMAL; 305 | 306 | adapter.SetJail(turn, 0); 307 | } 308 | else 309 | { 310 | players[turn].jail++; 311 | 312 | if (players[turn].jail >= 3) 313 | { 314 | Payment(turn, JAIL_PENALTY); 315 | 316 | players[turn].jail = 0; 317 | players[turn].state = Player.EState.NORMAL; 318 | 319 | adapter.SetJail(turn, 0); 320 | } 321 | } 322 | } 323 | } 324 | } 325 | 326 | if (players[turn].state == Player.EState.NORMAL) 327 | { 328 | bool notFinalDouble = (!isDouble) || (players[turn].doub <= 1); 329 | 330 | if (notFinalDouble) 331 | { 332 | Movement(d1 + d2, isDouble); 333 | } 334 | } 335 | 336 | //start turn again (unless retired or the double was from jail) 337 | if (players[turn].state != Player.EState.RETIRED && isDouble && !doubleInJail) 338 | { 339 | players[turn].doub++; 340 | 341 | if (players[turn].doub >= 3) 342 | { 343 | players[turn].position = JAIL_INDEX; 344 | players[turn].doub = 0; 345 | players[turn].state = Player.EState.JAIL; 346 | 347 | adapter.SetJail(turn, 1); 348 | } 349 | } 350 | 351 | EOutcome outcome = EndTurn((!isDouble || players[turn].state == Player.EState.RETIRED || players[turn].state == Player.EState.JAIL)); 352 | return outcome; 353 | } 354 | 355 | public EOutcome EndTurn(bool increment = true) 356 | { 357 | if (increment) 358 | { 359 | IncrementTurn(); 360 | 361 | int count = 0; 362 | 363 | while (players[turn].state == Player.EState.RETIRED && count <= PLAYER_COUNT * 2) 364 | { 365 | IncrementTurn(); 366 | count++; 367 | } 368 | 369 | if (remaining <= 1) 370 | { 371 | switch (turn) 372 | { 373 | case 0: return EOutcome.WIN1; 374 | case 1: return EOutcome.WIN2; 375 | case 2: return EOutcome.WIN3; 376 | case 3: return EOutcome.WIN4; 377 | } 378 | } 379 | } 380 | 381 | count++; 382 | 383 | if (count >= STALEMATE_TURN) 384 | { 385 | return EOutcome.DRAW; 386 | } 387 | 388 | return EOutcome.ONGOING; 389 | } 390 | 391 | public void IncrementTurn() 392 | { 393 | turn++; 394 | 395 | if (turn >= PLAYER_COUNT) 396 | { 397 | turn = 0; 398 | } 399 | } 400 | 401 | public void BeforeTurn() 402 | { 403 | if (players[turn].state == Player.EState.RETIRED) 404 | { 405 | return; 406 | } 407 | 408 | int itemCount = players[turn].items.Count; 409 | 410 | for (int j = 0; j < itemCount; j++) 411 | { 412 | int index = players[turn].items[j]; 413 | 414 | if (mortgaged[index]) 415 | { 416 | int advancePrice = (int)(COSTS[index] * MORTGAGE_INTEREST); 417 | 418 | if (advancePrice > players[turn].funds) 419 | { 420 | continue; 421 | } 422 | 423 | adapter.SetTurn(turn); 424 | 425 | adapter.SetSelectionState(index, 1); 426 | 427 | Player.EDecision decision = players[turn].DecideAdvance(index); 428 | 429 | adapter.SetSelectionState(index, 0); 430 | 431 | if (decision == Player.EDecision.YES) 432 | { 433 | Advance(index); 434 | } 435 | } 436 | else 437 | { 438 | adapter.SetTurn(turn); 439 | 440 | adapter.SetSelectionState(index, 1); 441 | 442 | Player.EDecision decision = players[turn].DecideMortgage(index); 443 | 444 | adapter.SetSelectionState(index, 0); 445 | 446 | if (decision == Player.EDecision.YES) 447 | { 448 | //Mortgage(index); 449 | } 450 | } 451 | } 452 | 453 | int[] sets = FindSets(turn); 454 | int setCount = sets.GetLength(0); 455 | 456 | for (int j = 0; j < setCount; j++) 457 | { 458 | int houseTotal = houses[SETS[sets[j], 0]] + houses[SETS[sets[j], 1]]; 459 | 460 | if (sets[j] != 0 && sets[j] != 7) 461 | { 462 | houseTotal += houses[SETS[sets[j], 2]]; 463 | } 464 | 465 | int sellMax = houseTotal; 466 | 467 | adapter.SetTurn(turn); 468 | 469 | adapter.SetSelectionState(SETS[sets[j], 0], 1); 470 | 471 | int decision = players[turn].DecideSellHouse(sets[j]); 472 | 473 | adapter.SetSelectionState(SETS[sets[j], 0], 0); 474 | 475 | decision = Math.Min(decision, sellMax); 476 | 477 | if (decision > 0) 478 | { 479 | SellHouses(sets[j], decision); 480 | players[turn].funds += (int)(decision * BUILD[property[SETS[sets[j], 0]]] * 0.5f); 481 | } 482 | } 483 | 484 | sets = FindSets(turn); 485 | setCount = sets.GetLength(0); 486 | 487 | for (int j = 0; j < setCount; j++) 488 | { 489 | int maxHouse = 10; 490 | int houseTotal = houses[SETS[sets[j], 0]] + houses[SETS[sets[j], 1]]; 491 | 492 | if (sets[j] != 0 && sets[j] != 7) 493 | { 494 | maxHouse = 15; 495 | houseTotal += houses[SETS[sets[j], 2]]; 496 | } 497 | 498 | int buildMax = maxHouse - houseTotal; 499 | int affordMax = (int)Math.Floor(players[turn].funds / (float)BUILD[property[SETS[sets[j], 0]]]); 500 | 501 | if (affordMax < 0) 502 | { 503 | affordMax = 0; 504 | } 505 | 506 | buildMax = Math.Min(buildMax, affordMax); 507 | 508 | adapter.SetTurn(turn); 509 | 510 | adapter.SetSelectionState(SETS[sets[j], 0], 1); 511 | 512 | int decision = players[turn].DecideBuildHouse(sets[j]); 513 | 514 | adapter.SetSelectionState(SETS[sets[j], 0], 0); 515 | 516 | decision = Math.Min(decision, buildMax); 517 | 518 | if (decision > 0) 519 | { 520 | BuildHouses(sets[j], decision); 521 | Payment(turn, decision * BUILD[property[SETS[sets[j], 0]]]); 522 | } 523 | } 524 | 525 | Trading(); 526 | } 527 | 528 | public void Trading() 529 | { 530 | List candidates = new List(); 531 | List candidates_index = new List(); 532 | 533 | for (int i = 0; i < PLAYER_COUNT; i++) 534 | { 535 | if (i == turn) 536 | { 537 | continue; 538 | } 539 | 540 | if (players[i].state == Player.EState.RETIRED) 541 | { 542 | continue; 543 | } 544 | 545 | candidates.Add(players[i]); 546 | candidates_index.Add(i); 547 | } 548 | 549 | if (candidates.Count == 0) 550 | { 551 | return; 552 | } 553 | 554 | int TRADE_ATTEMPTS = 4; 555 | int TRADE_ITEM_MAX = 5; 556 | int TRADE_MONEY_MAX = 500; 557 | 558 | for (int t = 0; t < TRADE_ATTEMPTS; t++) 559 | { 560 | int give = random.gen.Next(0, Math.Min(players[turn].items.Count, TRADE_ITEM_MAX)); 561 | 562 | int selectedPlayer = random.gen.Next(0, candidates.Count); 563 | 564 | Player other = candidates[selectedPlayer]; 565 | int other_index = candidates_index[selectedPlayer]; 566 | 567 | int recieve = random.gen.Next(0, Math.Min(other.items.Count, TRADE_ITEM_MAX)); 568 | 569 | if (players[turn].funds < 0 || other.funds < 0) 570 | { 571 | continue; 572 | } 573 | 574 | int moneyGive = random.gen.Next(0, Math.Min(players[turn].funds, TRADE_MONEY_MAX)); 575 | int moneyRecieve = random.gen.Next(0, Math.Min(other.funds, TRADE_MONEY_MAX)); 576 | int moneyBalance = moneyGive - moneyRecieve; 577 | 578 | if (give == 0 || recieve == 0) 579 | { 580 | continue; 581 | } 582 | 583 | List gift = new List(); 584 | List possible = new List(players[turn].items); 585 | 586 | for (int i = 0; i < give; i++) 587 | { 588 | int selection = random.gen.Next(0, possible.Count); 589 | 590 | gift.Add(possible[selection]); 591 | possible.RemoveAt(selection); 592 | } 593 | 594 | List returning = new List(); 595 | 596 | possible = new List(other.items); 597 | 598 | for (int i = 0; i < recieve; i++) 599 | { 600 | int selection = random.gen.Next(0, possible.Count); 601 | 602 | returning.Add(possible[selection]); 603 | possible.RemoveAt(selection); 604 | } 605 | 606 | //set neurons for trade 607 | for (int i = 0; i < gift.Count; i++) 608 | { 609 | adapter.SetSelectionState(gift[i], 1); 610 | } 611 | 612 | for (int i = 0; i < returning.Count; i++) 613 | { 614 | adapter.SetSelectionState(returning[i], 1); 615 | } 616 | 617 | adapter.SetMoneyContext(moneyBalance); 618 | 619 | Player.EDecision decision = players[turn].DecideOfferTrade(); 620 | 621 | if (decision == Player.EDecision.NO) 622 | { 623 | adapter.ClearSelectionState(); 624 | continue; 625 | } 626 | 627 | Player.EDecision decision2 = other.DecideAcceptTrade(); 628 | 629 | if (decision2 == Player.EDecision.NO) 630 | { 631 | continue; 632 | } 633 | 634 | for (int i = 0; i < gift.Count; i++) 635 | { 636 | Monopoly.Analytics.instance.MadeTrade(gift[i]); 637 | 638 | players[turn].items.Remove(gift[i]); 639 | other.items.Add(gift[i]); 640 | 641 | owners[gift[i]] = other_index; 642 | adapter.SetOwner(gift[i], other_index); 643 | } 644 | 645 | for (int i = 0; i < returning.Count; i++) 646 | { 647 | Monopoly.Analytics.instance.MadeTrade(returning[i]); 648 | 649 | other.items.Remove(returning[i]); 650 | players[turn].items.Add(returning[i]); 651 | 652 | owners[returning[i]] = turn; 653 | adapter.SetOwner(returning[i], turn); 654 | } 655 | 656 | adapter.ClearSelectionState(); 657 | 658 | players[turn].funds -= moneyBalance; 659 | other.funds += moneyBalance; 660 | } 661 | } 662 | 663 | public void Auction(int index) 664 | { 665 | bool[] participation = new bool[PLAYER_COUNT]; 666 | 667 | for (int i = 0; i < PLAYER_COUNT; i++) 668 | { 669 | participation[i] = players[i].state != Player.EState.RETIRED; 670 | } 671 | 672 | int[] bids = new int[PLAYER_COUNT]; 673 | 674 | for (int i = 0; i < PLAYER_COUNT; i++) 675 | { 676 | adapter.SetTurn(i); 677 | 678 | adapter.SetSelectionState(index, 1); 679 | 680 | bids[i] = players[i].DecideAuctionBid(index); 681 | 682 | adapter.SetSelectionState(index, 0); 683 | 684 | if (bids[i] > players[i].funds) 685 | { 686 | participation[i] = false; 687 | } 688 | } 689 | 690 | int max = 0; 691 | 692 | for (int i = 0; i < PLAYER_COUNT; i++) 693 | { 694 | if (participation[i]) 695 | { 696 | if (bids[i] > max) 697 | { 698 | max = bids[i]; 699 | } 700 | } 701 | } 702 | 703 | List candidates = new List(); 704 | List backup = new List(); 705 | 706 | for (int i = 0; i < PLAYER_COUNT; i++) 707 | { 708 | if (participation[i]) 709 | { 710 | if (bids[i] == max) 711 | { 712 | candidates.Add(i); 713 | } 714 | } 715 | 716 | if (players[i].state != Player.EState.RETIRED) 717 | { 718 | backup.Add(i); 719 | } 720 | } 721 | 722 | if (candidates.Count > 0) 723 | { 724 | int winner = candidates[random.gen.Next(0, candidates.Count)]; 725 | 726 | Payment(winner, max); 727 | 728 | owners[index] = winner; 729 | players[winner].items.Add(index); 730 | 731 | if (original[index] == -1) 732 | { 733 | original[index] = winner; 734 | } 735 | 736 | adapter.SetOwner(index, winner); 737 | } 738 | else 739 | { 740 | int winner = backup[random.gen.Next(0, backup.Count)]; 741 | 742 | owners[index] = winner; 743 | players[winner].items.Add(index); 744 | 745 | if (original[index] == -1) 746 | { 747 | original[index] = winner; 748 | } 749 | 750 | adapter.SetOwner(index, winner); 751 | } 752 | } 753 | 754 | public void Movement(int roll, bool isDouble) 755 | { 756 | players[turn].position += roll; 757 | 758 | //wrap around 759 | if (players[turn].position >= BOARD_LENGTH) 760 | { 761 | players[turn].position -= BOARD_LENGTH; 762 | 763 | if (players[turn].position == 0) 764 | { 765 | players[turn].funds += GO_BONUS; 766 | } 767 | else 768 | { 769 | players[turn].funds += GO_LANDING_BONUS; 770 | } 771 | } 772 | 773 | adapter.SetMoney(turn, players[turn].funds); 774 | adapter.SetPosition(turn, players[turn].position); 775 | 776 | ActivateTile(); 777 | } 778 | 779 | public void ActivateTile() 780 | { 781 | int index = players[turn].position; 782 | 783 | ETile tile = TYPES[index]; 784 | 785 | if (tile == ETile.PROPERTY) 786 | { 787 | int owner = Owner(index); 788 | 789 | if (owner == BANK_INDEX) 790 | { 791 | adapter.SetTurn(turn); 792 | adapter.SetSelection(index); 793 | Player.EBuyDecision decision = players[turn].DecideBuy(index); 794 | 795 | if (decision == Player.EBuyDecision.BUY) 796 | { 797 | if (players[turn].funds < COSTS[index]) 798 | { 799 | Auction(index); 800 | } 801 | else 802 | { 803 | Payment(turn, COSTS[index]); 804 | 805 | owners[index] = turn; 806 | 807 | if (original[index] == -1) 808 | { 809 | original[index] = turn; 810 | } 811 | 812 | players[turn].items.Add(index); 813 | 814 | adapter.SetOwner(index, owner); 815 | } 816 | 817 | } 818 | else if (decision == Player.EBuyDecision.AUCTION) 819 | { 820 | Auction(index); 821 | } 822 | } 823 | else if (owner == turn) 824 | { 825 | //do nothing 826 | } 827 | else if (!mortgaged[index]) 828 | { 829 | PaymentToPlayer(turn, owner, PROPERTY_PENALTIES[property[index], houses[index]]); 830 | } 831 | } 832 | else if (tile == ETile.TRAIN) 833 | { 834 | int owner = Owner(index); 835 | 836 | if (owner == BANK_INDEX) 837 | { 838 | adapter.SetTurn(turn); 839 | adapter.SetSelection(index); 840 | Player.EBuyDecision decision = players[turn].DecideBuy(index); 841 | 842 | if (decision == Player.EBuyDecision.BUY) 843 | { 844 | if (players[turn].funds < COSTS[index]) 845 | { 846 | Auction(index); 847 | } 848 | else 849 | { 850 | Payment(turn, COSTS[index]); 851 | 852 | owners[index] = turn; 853 | 854 | if (original[index] == -1) 855 | { 856 | original[index] = turn; 857 | } 858 | 859 | players[turn].items.Add(index); 860 | 861 | adapter.SetOwner(index, turn); 862 | } 863 | 864 | } 865 | else if (owner == turn) 866 | { 867 | //do nothing 868 | } 869 | else if (decision == Player.EBuyDecision.AUCTION) 870 | { 871 | Auction(index); 872 | } 873 | } 874 | else if (!mortgaged[index]) 875 | { 876 | //payment train 877 | int trains = CountTrains(owner); 878 | 879 | if (trains >= 1 && trains <= 4) 880 | { 881 | int fine = TRAIN_PENALTIES[trains - 1]; 882 | PaymentToPlayer(turn, owner, fine); 883 | } 884 | 885 | } 886 | } 887 | else if (tile == ETile.UTILITY) 888 | { 889 | int owner = Owner(index); 890 | 891 | if (owner == BANK_INDEX) 892 | { 893 | adapter.SetTurn(turn); 894 | 895 | adapter.SetSelectionState(index, 1); 896 | 897 | Player.EBuyDecision decision = players[turn].DecideBuy(index); 898 | 899 | adapter.SetSelectionState(index, 0); 900 | 901 | if (decision == Player.EBuyDecision.BUY) 902 | { 903 | if (players[turn].funds < COSTS[index]) 904 | { 905 | Auction(index); 906 | } 907 | else 908 | { 909 | Payment(turn, COSTS[index]); 910 | 911 | owners[index] = turn; 912 | 913 | if (original[index] == -1) 914 | { 915 | original[index] = turn; 916 | } 917 | 918 | players[turn].items.Add(index); 919 | 920 | adapter.SetOwner(index, turn); 921 | } 922 | 923 | } 924 | else if (decision == Player.EBuyDecision.AUCTION) 925 | { 926 | Auction(index); 927 | } 928 | } 929 | else if (owner == turn) 930 | { 931 | //do nothing 932 | } 933 | else if (!mortgaged[index]) 934 | { 935 | //payment utility 936 | int utilities = CountUtilities(owner); 937 | 938 | if (utilities >= 1 && utilities <= 2) 939 | { 940 | int fine = UTILITY_PENALTIES[utilities - 1] * last_roll; 941 | PaymentToPlayer(turn, owner, fine); 942 | } 943 | } 944 | } 945 | else if (tile == ETile.TAX) 946 | { 947 | Payment(turn, COSTS[index]); 948 | } 949 | else if (tile == ETile.CHANCE) 950 | { 951 | DrawChance(); 952 | } 953 | else if (tile == ETile.CHEST) 954 | { 955 | DrawChest(); 956 | } 957 | else if (tile == ETile.JAIL) 958 | { 959 | players[turn].position = JAIL_INDEX; 960 | players[turn].doub = 0; 961 | players[turn].state = Player.EState.JAIL; 962 | 963 | adapter.SetJail(turn, 1); 964 | } 965 | } 966 | 967 | public void Payment(int owner, int fine) 968 | { 969 | players[owner].funds -= fine; 970 | adapter.SetMoney(owner, players[owner].funds); 971 | 972 | int original = players[owner].funds; 973 | 974 | //prompt for selling sets 975 | if (players[owner].funds < 0) 976 | { 977 | int[] sets = FindSets(turn); 978 | int setCount = sets.GetLength(0); 979 | 980 | for (int j = 0; j < setCount; j++) 981 | { 982 | int houseTotal = houses[SETS[sets[j], 0]] + houses[SETS[sets[j], 1]]; 983 | 984 | if (sets[j] != 0 && sets[j] != 7) 985 | { 986 | houseTotal += houses[SETS[sets[j], 2]]; 987 | } 988 | 989 | int sellMax = houseTotal; 990 | 991 | adapter.SetTurn(turn); 992 | 993 | adapter.SetSelectionState(SETS[sets[j], 0], 1); 994 | 995 | int decision = players[turn].DecideSellHouse(sets[j]); 996 | 997 | adapter.SetSelectionState(SETS[sets[j], 0], 0); 998 | 999 | decision = Math.Min(decision, sellMax); 1000 | 1001 | if (decision > 0) 1002 | { 1003 | SellHouses(sets[j], decision); 1004 | 1005 | players[owner].funds += (int)(decision * BUILD[property[SETS[sets[j], 0]]] * 0.5f); 1006 | adapter.SetMoney(owner, players[owner].funds); 1007 | } 1008 | } 1009 | } 1010 | 1011 | //prompt for mortgages once 1012 | if (players[owner].funds < 0) 1013 | { 1014 | int itemCount = players[owner].items.Count; 1015 | 1016 | for (int i = 0; i < itemCount; i++) 1017 | { 1018 | int item = players[owner].items[i]; 1019 | adapter.SetTurn(owner); 1020 | 1021 | adapter.SetSelectionState(players[owner].items[i], 1); 1022 | 1023 | Player.EDecision decision = players[owner].DecideMortgage(players[owner].items[i]); 1024 | 1025 | adapter.SetSelectionState(players[owner].items[i], 0); 1026 | 1027 | if (decision == Player.EDecision.YES) 1028 | { 1029 | Mortgage(item); 1030 | } 1031 | } 1032 | } 1033 | 1034 | //bankrupt 1035 | if (players[owner].funds < 0) 1036 | { 1037 | int regained = players[owner].funds - original; 1038 | 1039 | int itemCount = players[owner].items.Count; 1040 | 1041 | int housemoney = 0; 1042 | 1043 | for (int i = 0; i < itemCount; i++) 1044 | { 1045 | int item = players[owner].items[i]; 1046 | owners[item] = BANK_INDEX; 1047 | adapter.SetOwner(item, BANK_INDEX); 1048 | 1049 | if (houses[item] > 0) 1050 | { 1051 | int liquidated = houses[item]; 1052 | int sell = (liquidated * BUILD[property[item]]) / 2; 1053 | housemoney += sell; 1054 | 1055 | houses[item] = 0; 1056 | } 1057 | } 1058 | 1059 | players[owner].items.Clear(); 1060 | 1061 | //give money to other 1062 | players[owner].state = Player.EState.RETIRED; 1063 | remaining--; 1064 | } 1065 | } 1066 | 1067 | public void PaymentToPlayer(int owner, int recipient, int fine) 1068 | { 1069 | players[owner].funds -= fine; 1070 | adapter.SetMoney(owner, players[owner].funds); 1071 | 1072 | players[recipient].funds += fine; 1073 | adapter.SetMoney(recipient, players[recipient].funds); 1074 | 1075 | int original = players[owner].funds; 1076 | 1077 | //prompt for selling sets 1078 | if (players[owner].funds < 0) 1079 | { 1080 | int[] sets = FindSets(turn); 1081 | int setCount = sets.GetLength(0); 1082 | 1083 | for (int j = 0; j < setCount; j++) 1084 | { 1085 | int houseTotal = houses[SETS[sets[j], 0]] + houses[SETS[sets[j], 1]]; 1086 | 1087 | if (sets[j] != 0 && sets[j] != 7) 1088 | { 1089 | houseTotal += houses[SETS[sets[j], 2]]; 1090 | } 1091 | 1092 | int sellMax = houseTotal; 1093 | 1094 | adapter.SetTurn(turn); 1095 | 1096 | adapter.SetSelectionState(SETS[sets[j], 0], 1); 1097 | 1098 | int decision = players[turn].DecideSellHouse(sets[j]); 1099 | 1100 | adapter.SetSelectionState(SETS[sets[j], 0], 0); 1101 | 1102 | decision = Math.Min(decision, sellMax); 1103 | 1104 | if (decision > 0) 1105 | { 1106 | SellHouses(sets[j], decision); 1107 | players[owner].funds += (int)(decision * BUILD[property[SETS[sets[j], 0]]] * 0.5f); 1108 | 1109 | adapter.SetMoney(owner, players[owner].funds); 1110 | } 1111 | } 1112 | } 1113 | 1114 | //prompt for mortgages once 1115 | if (players[owner].funds < 0) 1116 | { 1117 | int itemCount = players[owner].items.Count; 1118 | 1119 | for (int i = 0; i < itemCount; i++) 1120 | { 1121 | int item = players[owner].items[i]; 1122 | adapter.SetTurn(owner); 1123 | 1124 | adapter.SetSelectionState(players[owner].items[i], 0); 1125 | 1126 | Player.EDecision decision = players[owner].DecideMortgage(players[owner].items[i]); 1127 | 1128 | adapter.SetSelectionState(players[owner].items[i], 1); 1129 | 1130 | if (decision == Player.EDecision.YES) 1131 | { 1132 | Mortgage(item); 1133 | } 1134 | } 1135 | } 1136 | 1137 | //bankrupt 1138 | if (players[owner].funds < 0) 1139 | { 1140 | players[recipient].funds += players[owner].funds; 1141 | adapter.SetMoney(recipient, players[recipient].funds); 1142 | 1143 | int itemCount = players[owner].items.Count; 1144 | 1145 | int housemoney = 0; 1146 | 1147 | for (int i = 0; i < itemCount; i++) 1148 | { 1149 | //give to other player 1150 | players[recipient].items.Add(players[owner].items[i]); 1151 | 1152 | adapter.SetOwner(players[owner].items[i], recipient); 1153 | 1154 | int item = players[owner].items[i]; 1155 | owners[item] = recipient; 1156 | 1157 | if (houses[item] > 0) 1158 | { 1159 | int liquidated = houses[item]; 1160 | int sell = (liquidated * BUILD[property[item]]) / 2; 1161 | housemoney += sell; 1162 | 1163 | houses[item] = 0; 1164 | } 1165 | } 1166 | 1167 | players[recipient].funds += housemoney; 1168 | adapter.SetMoney(recipient, players[recipient].funds); 1169 | 1170 | players[owner].items.Clear(); 1171 | 1172 | //give money to other 1173 | players[owner].state = Player.EState.RETIRED; 1174 | remaining--; 1175 | } 1176 | } 1177 | 1178 | public int Owner(int index) 1179 | { 1180 | return owners[index]; 1181 | } 1182 | 1183 | public void Mortgage(int index) 1184 | { 1185 | mortgaged[index] = true; 1186 | adapter.SetMortgage(index, 1); 1187 | 1188 | players[owners[index]].funds += COSTS[index] / 2; 1189 | adapter.SetMoney(owners[index], players[owners[index]].funds); 1190 | } 1191 | 1192 | public void Advance(int index) 1193 | { 1194 | mortgaged[index] = false; 1195 | adapter.SetMortgage(index, 0); 1196 | 1197 | int cost = (int)(COSTS[index] * MORTGAGE_INTEREST); 1198 | Payment(owners[index], cost); 1199 | } 1200 | 1201 | public int CountTrains(int player) 1202 | { 1203 | int itemCount = players[player].items.Count; 1204 | 1205 | int count = 0; 1206 | 1207 | for (int i = 0; i < itemCount; i++) 1208 | { 1209 | if (TRAIN_POSITIONS.Contains(players[player].items[i])) 1210 | { 1211 | count++; 1212 | } 1213 | } 1214 | 1215 | return count; 1216 | } 1217 | 1218 | public int CountUtilities(int player) 1219 | { 1220 | int itemCount = players[player].items.Count; 1221 | 1222 | int count = 0; 1223 | 1224 | for (int i = 0; i < itemCount; i++) 1225 | { 1226 | if (UTILITY_POSIIONS.Contains(players[player].items[i])) 1227 | { 1228 | count++; 1229 | } 1230 | } 1231 | 1232 | return count; 1233 | } 1234 | 1235 | public void DrawChance() 1236 | { 1237 | CardEntry card = chance[0]; 1238 | chance.RemoveAt(0); 1239 | chance.Add(card); 1240 | 1241 | if (card.card == ECard.ADVANCE) 1242 | { 1243 | if (players[turn].position > card.val) 1244 | { 1245 | players[turn].funds += GO_BONUS; 1246 | adapter.SetMoney(turn, players[turn].funds); 1247 | } 1248 | 1249 | players[turn].position = card.val; 1250 | adapter.SetPosition(turn, players[turn].position); 1251 | 1252 | ActivateTile(); 1253 | } 1254 | else if (card.card == ECard.REWARD) 1255 | { 1256 | players[turn].funds += card.val; 1257 | adapter.SetMoney(turn, players[turn].funds); 1258 | } 1259 | else if (card.card == ECard.FINE) 1260 | { 1261 | Payment(turn, card.val); 1262 | } 1263 | else if (card.card == ECard.BACK3) 1264 | { 1265 | players[turn].position -= 3; 1266 | adapter.SetPosition(turn, players[turn].position); 1267 | 1268 | ActivateTile(); 1269 | } 1270 | else if (card.card == ECard.CARD) 1271 | { 1272 | players[turn].card++; 1273 | adapter.SetCard(turn, players[turn].card); 1274 | } 1275 | else if (card.card == ECard.JAIL) 1276 | { 1277 | players[turn].position = JAIL_INDEX; 1278 | players[turn].doub = 0; 1279 | players[turn].state = Player.EState.JAIL; 1280 | 1281 | adapter.SetPosition(turn, players[turn].position); 1282 | adapter.SetJail(turn, 1); 1283 | } 1284 | else if (card.card == ECard.RAILROAD2) 1285 | { 1286 | AdvanceToTrain2(); 1287 | } 1288 | else if (card.card == ECard.UTILITY10) 1289 | { 1290 | AdvanceToUtility10(); 1291 | } 1292 | else if (card.card == ECard.CHAIRMAN) 1293 | { 1294 | for (int i = 0; i < PLAYER_COUNT; i++) 1295 | { 1296 | if (i == turn) 1297 | { 1298 | continue; 1299 | } 1300 | 1301 | //only pay active players 1302 | if (players[i].state != Player.EState.RETIRED) 1303 | { 1304 | PaymentToPlayer(turn, i, 50); 1305 | } 1306 | } 1307 | } 1308 | else if (card.card == ECard.REPAIRS) 1309 | { 1310 | int houseCount = 0; 1311 | int hotelCount = 0; 1312 | int itemCount = players[turn].items.Count; 1313 | 1314 | for (int i = 0; i < itemCount; i++) 1315 | { 1316 | int index = players[turn].items[i]; 1317 | 1318 | if (houses[index] <= 4) 1319 | { 1320 | houseCount += houses[index]; 1321 | } 1322 | else 1323 | { 1324 | hotelCount++; 1325 | } 1326 | } 1327 | 1328 | Payment(turn, houseCount * 25 + hotelCount * 100); 1329 | } 1330 | } 1331 | 1332 | public void DrawChest() 1333 | { 1334 | CardEntry card = chest[0]; 1335 | chest.RemoveAt(0); 1336 | chest.Add(card); 1337 | 1338 | if (card.card == ECard.ADVANCE) 1339 | { 1340 | if (players[turn].position > card.val) 1341 | { 1342 | players[turn].funds += GO_BONUS; 1343 | adapter.SetMoney(turn, players[turn].funds); 1344 | } 1345 | 1346 | players[turn].position = card.val; 1347 | adapter.SetPosition(turn, players[turn].position); 1348 | 1349 | ActivateTile(); 1350 | } 1351 | else if (card.card == ECard.REWARD) 1352 | { 1353 | players[turn].funds += card.val; 1354 | adapter.SetMoney(turn, players[turn].funds); 1355 | } 1356 | else if (card.card == ECard.FINE) 1357 | { 1358 | Payment(turn, card.val); 1359 | } 1360 | else if (card.card == ECard.CARD) 1361 | { 1362 | players[turn].card++; 1363 | adapter.SetCard(turn, players[turn].card); 1364 | } 1365 | else if (card.card == ECard.JAIL) 1366 | { 1367 | players[turn].position = JAIL_INDEX; 1368 | players[turn].doub = 0; 1369 | players[turn].state = Player.EState.JAIL; 1370 | 1371 | adapter.SetPosition(turn, players[turn].position); 1372 | adapter.SetJail(turn, 1); 1373 | } 1374 | else if (card.card == ECard.BIRTHDAY) 1375 | { 1376 | for (int i = 0; i < PLAYER_COUNT; i++) 1377 | { 1378 | if (i == turn) 1379 | { 1380 | continue; 1381 | } 1382 | 1383 | //only pay active players 1384 | if (players[i].state != Player.EState.RETIRED) 1385 | { 1386 | PaymentToPlayer(i, turn, 10); 1387 | } 1388 | } 1389 | } 1390 | else if (card.card == ECard.STREET) 1391 | { 1392 | int houseCount = 0; 1393 | int hotelCount = 0; 1394 | int itemCount = players[turn].items.Count; 1395 | 1396 | for (int i = 0; i < itemCount; i++) 1397 | { 1398 | int index = players[turn].items[i]; 1399 | 1400 | if (houses[index] <= 4) 1401 | { 1402 | houseCount += houses[index]; 1403 | } 1404 | else 1405 | { 1406 | hotelCount++; 1407 | } 1408 | } 1409 | 1410 | Payment(turn, houseCount * 40 + hotelCount * 115); 1411 | } 1412 | } 1413 | 1414 | public void AdvanceToTrain2() 1415 | { 1416 | int index = players[turn].position; 1417 | 1418 | if (index < TRAIN_POSITIONS[0]) 1419 | { 1420 | players[turn].position = TRAIN_POSITIONS[0]; 1421 | } 1422 | else if (index < TRAIN_POSITIONS[1]) 1423 | { 1424 | players[turn].position = TRAIN_POSITIONS[1]; 1425 | } 1426 | else if (index < TRAIN_POSITIONS[2]) 1427 | { 1428 | players[turn].position = TRAIN_POSITIONS[2]; 1429 | } 1430 | else if (index < TRAIN_POSITIONS[3]) 1431 | { 1432 | players[turn].position = TRAIN_POSITIONS[3]; 1433 | } 1434 | else 1435 | { 1436 | players[turn].position = TRAIN_POSITIONS[0]; 1437 | players[turn].funds += GO_BONUS; 1438 | adapter.SetMoney(turn, players[turn].funds); 1439 | } 1440 | 1441 | adapter.SetPosition(turn, players[turn].position); 1442 | 1443 | index = players[turn].position; 1444 | 1445 | int owner = Owner(index); 1446 | 1447 | if (owner == BANK_INDEX) 1448 | { 1449 | adapter.SetTurn(turn); 1450 | 1451 | adapter.SetSelectionState(index, 0); 1452 | 1453 | Player.EBuyDecision decision = players[turn].DecideBuy(index); 1454 | 1455 | adapter.SetSelectionState(index, 1); 1456 | 1457 | if (decision == Player.EBuyDecision.BUY) 1458 | { 1459 | if (players[turn].funds < COSTS[index]) 1460 | { 1461 | Auction(index); 1462 | } 1463 | else 1464 | { 1465 | Payment(turn, COSTS[index]); 1466 | 1467 | owners[index] = turn; 1468 | 1469 | if (original[index] == -1) 1470 | { 1471 | original[index] = turn; 1472 | } 1473 | 1474 | players[turn].items.Add(index); 1475 | 1476 | adapter.SetOwner(index, turn); 1477 | } 1478 | 1479 | 1480 | } 1481 | else if (decision == Player.EBuyDecision.AUCTION) 1482 | { 1483 | Auction(index); 1484 | } 1485 | } 1486 | else if (owner == turn) 1487 | { 1488 | //do nothing 1489 | } 1490 | else if (!mortgaged[index]) 1491 | { 1492 | //payment train 1493 | int trains = CountTrains(owner); 1494 | 1495 | if (trains >= 1 && trains <= 4) 1496 | { 1497 | int fine = TRAIN_PENALTIES[trains - 1]; 1498 | 1499 | PaymentToPlayer(turn, owner, fine * 2); 1500 | } 1501 | } 1502 | } 1503 | 1504 | public void AdvanceToUtility10() 1505 | { 1506 | int index = players[turn].position; 1507 | 1508 | if (index < UTILITY_POSIIONS[0]) 1509 | { 1510 | players[turn].position = UTILITY_POSIIONS[0]; 1511 | } 1512 | else if (index < UTILITY_POSIIONS[1]) 1513 | { 1514 | players[turn].position = UTILITY_POSIIONS[1]; 1515 | } 1516 | else 1517 | { 1518 | players[turn].position = UTILITY_POSIIONS[0]; 1519 | players[turn].funds += GO_BONUS; 1520 | 1521 | adapter.SetMoney(turn, players[turn].funds); 1522 | } 1523 | 1524 | adapter.SetPosition(turn, players[turn].position); 1525 | 1526 | index = players[turn].position; 1527 | 1528 | int owner = Owner(index); 1529 | 1530 | if (owner == BANK_INDEX) 1531 | { 1532 | adapter.SetTurn(turn); 1533 | Player.EBuyDecision decision = players[turn].DecideBuy(index); 1534 | 1535 | if (decision == Player.EBuyDecision.BUY) 1536 | { 1537 | if (players[turn].funds < COSTS[index]) 1538 | { 1539 | Auction(index); 1540 | } 1541 | else 1542 | { 1543 | Payment(turn, COSTS[index]); 1544 | 1545 | owners[index] = turn; 1546 | 1547 | if (original[index] == -1) 1548 | { 1549 | original[index] = turn; 1550 | } 1551 | 1552 | players[turn].items.Add(index); 1553 | 1554 | adapter.SetOwner(index, turn); 1555 | } 1556 | } 1557 | else if (decision == Player.EBuyDecision.AUCTION) 1558 | { 1559 | Auction(index); 1560 | } 1561 | } 1562 | else if (owner == turn) 1563 | { 1564 | //do nothing 1565 | } 1566 | else if (!mortgaged[index]) 1567 | { 1568 | //payment utility 1569 | int fine = 10 * last_roll; 1570 | 1571 | PaymentToPlayer(turn, owner, fine); 1572 | } 1573 | } 1574 | 1575 | public int[] FindSets(int owner) 1576 | { 1577 | List sets = new List(); 1578 | List items = players[owner].items; 1579 | 1580 | for (int i = 0; i < 8; i++) 1581 | { 1582 | //two piece sets 1583 | if (i == 0 || i == 7) 1584 | { 1585 | if (items.Contains(SETS[i,0]) && items.Contains(SETS[i, 1])) 1586 | { 1587 | sets.Add(i); 1588 | } 1589 | 1590 | continue; 1591 | } 1592 | 1593 | //three piece sets 1594 | if (items.Contains(SETS[i, 0]) && items.Contains(SETS[i, 1]) && items.Contains(SETS[i, 2])) 1595 | { 1596 | sets.Add(i); 1597 | } 1598 | } 1599 | 1600 | return sets.ToArray(); 1601 | } 1602 | 1603 | public void BuildHouses(int set, int amount) 1604 | { 1605 | int last = 2; 1606 | 1607 | if (set == 0 || set == 7) 1608 | { 1609 | last = 1; 1610 | } 1611 | 1612 | for (int i = 0; i < amount; i++) 1613 | { 1614 | //find smallest house number from back 1615 | int bj = last; 1616 | 1617 | for (int j = last - 1; j >= 0; j--) 1618 | { 1619 | if (houses[SETS[set, bj]] > houses[SETS[set, j]]) 1620 | { 1621 | bj = j; 1622 | } 1623 | } 1624 | 1625 | houses[SETS[set, bj]]++; 1626 | adapter.SetHouse(SETS[set, bj], houses[SETS[set, bj]]); 1627 | } 1628 | } 1629 | 1630 | public void SellHouses(int set, int amount) 1631 | { 1632 | int last = 2; 1633 | 1634 | if (set == 0 || set == 7) 1635 | { 1636 | last = 1; 1637 | } 1638 | 1639 | for (int i = 0; i < amount; i++) 1640 | { 1641 | //find smallest house number from back 1642 | int bj = 0; 1643 | 1644 | for (int j = 0; j <= last; j++) 1645 | { 1646 | if (houses[SETS[set, bj]] < houses[SETS[set, j]]) 1647 | { 1648 | bj = j; 1649 | } 1650 | } 1651 | 1652 | houses[SETS[set, bj]]--; 1653 | adapter.SetHouse(SETS[set, bj], houses[SETS[set, bj]]); 1654 | } 1655 | } 1656 | } 1657 | } 1658 | -------------------------------------------------------------------------------- /Monopoly/Monopoly/NeuralPlayer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MONOPOLY 8 | { 9 | 10 | public class NeuralPlayer : Player 11 | { 12 | public NEAT.Phenotype network; 13 | public NetworkAdapter adapter; 14 | 15 | public NeuralPlayer() 16 | { 17 | items = new List(); 18 | } 19 | 20 | public override EBuyDecision DecideBuy(int index) 21 | { 22 | float[] Y = network.Propagate(adapter.pack); 23 | 24 | if (Y[0] > 0.5f) 25 | { 26 | return EBuyDecision.BUY; 27 | } 28 | 29 | return EBuyDecision.AUCTION; 30 | } 31 | 32 | public override EJailDecision DecideJail() 33 | { 34 | float[] Y = network.Propagate(adapter.pack); 35 | 36 | if (Y[1] < 0.333f) 37 | { 38 | return EJailDecision.CARD; 39 | } 40 | else if (Y[1] < 0.666f) 41 | { 42 | return EJailDecision.ROLL; 43 | } 44 | 45 | return EJailDecision.PAY; 46 | } 47 | 48 | public override EDecision DecideMortgage(int index) 49 | { 50 | float[] Y = network.Propagate(adapter.pack); 51 | 52 | if (Y[2] > 0.5f) 53 | { 54 | return EDecision.YES; 55 | } 56 | 57 | return EDecision.NO; 58 | } 59 | 60 | public override EDecision DecideAdvance(int index) 61 | { 62 | float[] Y = network.Propagate(adapter.pack); 63 | 64 | if (Y[3] > 0.5f) 65 | { 66 | return EDecision.YES; 67 | } 68 | 69 | return EDecision.NO; 70 | } 71 | 72 | public override int DecideAuctionBid(int index) 73 | { 74 | float[] Y = network.Propagate(adapter.pack); 75 | 76 | float result = Y[4]; 77 | float money = adapter.ConvertMoneyValue(result); 78 | 79 | Monopoly.Analytics.instance.MakeBid(index, (int)money); 80 | 81 | return (int)money; 82 | } 83 | 84 | public override int DecideBuildHouse(int set) 85 | { 86 | float[] Y = network.Propagate(adapter.pack); 87 | 88 | float result = Y[5]; 89 | float money = adapter.ConvertHouseValue(result); 90 | 91 | return (int)money; 92 | } 93 | 94 | public override int DecideSellHouse(int set) 95 | { 96 | float[] Y = network.Propagate(adapter.pack); 97 | 98 | float result = Y[6]; 99 | float money = adapter.ConvertHouseValue(result); 100 | 101 | return (int)money; 102 | } 103 | 104 | public override EDecision DecideOfferTrade() 105 | { 106 | float[] Y = network.Propagate(adapter.pack); 107 | 108 | if (Y[7] > 0.5f) 109 | { 110 | return EDecision.YES; 111 | } 112 | 113 | return EDecision.NO; 114 | } 115 | 116 | public override EDecision DecideAcceptTrade() 117 | { 118 | float[] Y = network.Propagate(adapter.pack); 119 | 120 | if (Y[8] > 0.5f) 121 | { 122 | return EDecision.YES; 123 | } 124 | 125 | return EDecision.NO; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Monopoly/Monopoly/Player.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MONOPOLY 8 | { 9 | public class Player 10 | { 11 | public enum EState 12 | { 13 | NORMAL, 14 | JAIL, 15 | RETIRED, 16 | } 17 | 18 | public enum EBuyDecision 19 | { 20 | BUY, 21 | AUCTION, 22 | } 23 | 24 | public enum EJailDecision 25 | { 26 | ROLL, 27 | PAY, 28 | CARD 29 | } 30 | 31 | public enum EDecision 32 | { 33 | YES, 34 | NO 35 | } 36 | 37 | public EState state = EState.NORMAL; 38 | 39 | public int position = 0; 40 | public int funds = 1500; 41 | 42 | public int jail = 0; 43 | public int doub = 0; 44 | public int card = 0; 45 | 46 | public List items; 47 | 48 | public Player() 49 | { 50 | items = new List(); 51 | } 52 | 53 | public virtual EBuyDecision DecideBuy(int index) 54 | { 55 | return EBuyDecision.BUY; 56 | } 57 | 58 | public virtual EJailDecision DecideJail() 59 | { 60 | return EJailDecision.ROLL; 61 | } 62 | 63 | public virtual EDecision DecideMortgage(int index) 64 | { 65 | if (funds < 0) 66 | { 67 | return EDecision.YES; 68 | } 69 | 70 | return EDecision.NO; 71 | } 72 | 73 | public virtual EDecision DecideAdvance(int index) 74 | { 75 | return EDecision.YES; 76 | } 77 | 78 | public virtual int DecideAuctionBid(int index) 79 | { 80 | return Board.COSTS[index]; 81 | } 82 | 83 | public virtual int DecideBuildHouse(int set) 84 | { 85 | return 15; 86 | } 87 | 88 | public virtual int DecideSellHouse(int set) 89 | { 90 | if (funds < 0) 91 | { 92 | return 15; 93 | } 94 | 95 | return 0; 96 | } 97 | 98 | public virtual EDecision DecideOfferTrade() 99 | { 100 | return EDecision.NO; 101 | } 102 | 103 | public virtual EDecision DecideAcceptTrade() 104 | { 105 | return EDecision.NO; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Monopoly/NetworkAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | public class NetworkAdapter 8 | { 9 | public float[] pack; 10 | 11 | public int turn = 0; 12 | public int pos = 4; 13 | public int mon = 8; 14 | public int card = 12; 15 | public int jail = 16; 16 | public int own = 20; 17 | public int mort = 48; 18 | public int house = 76; 19 | public int select = 98; 20 | public int select_money = 126; 21 | 22 | public int[] PROPS = new int[40] { -1, 0, -1, 1, -1, 2, 3, -1, 4, 5, -1, 6, 7, 8, 9, 10, 11, -1, 12, 13, -1, 14, -1, 15, 16, 17, 18, 19, 20, 21, -1, 22, 23, -1, 24, 25, -1, 26, -1, 27 }; 23 | public int[] HOUSES = new int[40] { -1, 0, -1, 1, -1, -1, 2, -1, 3, 4, -1, 5, -1, 6, 7, -1, 8, -1, 9, 10, -1, 11, -1, 12, 13, -1, 14, 15, -1, 16, -1, 17, 18, -1, 19, -1, -1, 20, -1, 21 }; 24 | 25 | public NetworkAdapter() 26 | { 27 | pack = new float[127]; 28 | } 29 | 30 | public void Reset() 31 | { 32 | pack = new float[127]; 33 | } 34 | 35 | public float ConvertMoney(int money) 36 | { 37 | float norm = (float)money / 4000.0f; 38 | float clamp = Math.Clamp(norm, 0.0f, 1.0f); 39 | 40 | return clamp; 41 | } 42 | 43 | public float ConvertMoneyValue(float value) 44 | { 45 | return value * 4000.0f; 46 | } 47 | 48 | public float ConvertHouseValue(float value) 49 | { 50 | if (value <= 0.5f) 51 | { 52 | value = 0.0f; 53 | } 54 | 55 | return value * 15.0f; 56 | } 57 | 58 | public float ConvertPosition(int position) 59 | { 60 | float norm = (float)position / 39.0f; 61 | float clamp = Math.Clamp(norm, 0.0f, 1.0f); 62 | 63 | return clamp; 64 | } 65 | 66 | public float ConvertCard(int cards) 67 | { 68 | float clamp = Math.Clamp(card, 0.0f, 1.0f); 69 | return clamp; 70 | } 71 | 72 | public float ConvertHouse(int houses) 73 | { 74 | float norm = (float)houses / 5.0f; 75 | float clamp = Math.Clamp(norm, 0.0f, 1.0f); 76 | 77 | return clamp; 78 | } 79 | 80 | public void SetTurn(int index) 81 | { 82 | for (int i = 0; i < 4; i++) 83 | { 84 | pack[i] = 0.0f; 85 | } 86 | 87 | pack[index] = 1.0f; 88 | } 89 | 90 | public void SetSelection(int index) 91 | { 92 | for (int i = select; i < select + 29; i++) 93 | { 94 | pack[i] = 0.0f; 95 | } 96 | 97 | pack[select + PROPS[index]] = 1.0f; 98 | } 99 | 100 | public void SetSelectionState(int index, int state) 101 | { 102 | pack[select + PROPS[index]] = state; 103 | } 104 | 105 | public void SetMoneyContext(int state) 106 | { 107 | pack[select_money] = state; 108 | } 109 | 110 | public void ClearSelectionState() 111 | { 112 | for (int i = select; i < select + 29; i++) 113 | { 114 | pack[i] = 0.0f; 115 | } 116 | } 117 | 118 | public void SetPosition(int index, int position) 119 | { 120 | pack[pos + index] = ConvertPosition(position); 121 | } 122 | 123 | public void SetMoney(int index, int money) 124 | { 125 | pack[mon + index] = ConvertMoney(money); 126 | } 127 | 128 | public void SetCard(int index, int cards) 129 | { 130 | pack[card + index] = ConvertCard(cards); 131 | } 132 | 133 | public void SetJail(int index, int state) 134 | { 135 | pack[jail + index] = state; 136 | } 137 | 138 | public void SetOwner(int property, int state) 139 | { 140 | float convert = (state + 1) / 4.0f; 141 | 142 | pack[own + PROPS[property]] = convert; 143 | } 144 | 145 | public void SetMortgage(int property, int state) 146 | { 147 | pack[mort + PROPS[property]] = state; 148 | } 149 | 150 | public void SetHouse(int property, int houses) 151 | { 152 | pack[house + HOUSES[property]] = ConvertHouse(houses); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /Monopoly/NeuroEvolution/Crossover.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace NEAT 8 | { 9 | public class Crossover 10 | { 11 | public static Crossover instance = null; 12 | 13 | public float CROSSOVER_CHANCE = 0.75f; 14 | 15 | public float C1 = 1.0f; 16 | public float C2 = 1.0f; 17 | public float C3 = 0.4f; 18 | public float DISTANCE = 1.0f; 19 | 20 | public static void Initialise() 21 | { 22 | if (instance == null) 23 | { 24 | instance = new Crossover(); 25 | } 26 | } 27 | 28 | public Crossover() 29 | { 30 | 31 | } 32 | 33 | public Genotype ProduceOffspring(Genotype first, Genotype second) 34 | { 35 | List copy_first = new List(); 36 | List copy_second = new List(); 37 | 38 | copy_first.AddRange(first.edges); 39 | copy_second.AddRange(second.edges); 40 | 41 | List match_first = new List(); 42 | List match_second = new List(); 43 | 44 | List disjoint_first = new List(); 45 | List disjoint_second = new List(); 46 | 47 | List excess_first = new List(); 48 | List excess_second = new List(); 49 | 50 | int genes_first = first.edges.Count; 51 | int genes_second = second.edges.Count; 52 | 53 | int invmax_first = first.edges[first.edges.Count - 1].innovation; 54 | int invmax_second = second.edges[second.edges.Count - 1].innovation; 55 | 56 | int invmin = invmax_first > invmax_second ? invmax_second : invmax_first; 57 | 58 | for (int i = 0; i < genes_first; i++) 59 | { 60 | for (int j = 0; j < genes_second; j++) 61 | { 62 | EdgeInfo info_first = copy_first[i]; 63 | EdgeInfo info_second = copy_second[j]; 64 | 65 | //matching genes 66 | if (info_first.innovation == info_second.innovation) 67 | { 68 | match_first.Add(info_first); 69 | match_second.Add(info_second); 70 | 71 | copy_first.Remove(info_first); 72 | copy_second.Remove(info_second); 73 | 74 | i--; 75 | genes_first--; 76 | genes_second--; 77 | break; 78 | } 79 | } 80 | } 81 | 82 | for (int i = 0; i < copy_first.Count; i++) 83 | { 84 | if (copy_first[i].innovation > invmin) 85 | { 86 | excess_first.Add(copy_first[i]); 87 | } 88 | else 89 | { 90 | disjoint_first.Add(copy_first[i]); 91 | } 92 | } 93 | 94 | for (int i = 0; i < copy_second.Count; i++) 95 | { 96 | if (copy_second[i].innovation > invmin) 97 | { 98 | excess_second.Add(copy_second[i]); 99 | } 100 | else 101 | { 102 | disjoint_second.Add(copy_second[i]); 103 | } 104 | } 105 | 106 | Genotype child = new Genotype(); 107 | 108 | int matching = match_first.Count; 109 | 110 | for (int i = 0; i < matching; i++) 111 | { 112 | int roll = RNG.instance.gen.Next(0, 2); 113 | 114 | if (roll == 0 || !match_second[i].enabled) 115 | { 116 | child.AddEdge(match_first[i].source, match_first[i].destination, match_first[i].weight, match_first[i].enabled, match_first[i].innovation); 117 | } 118 | else 119 | { 120 | child.AddEdge(match_second[i].source, match_second[i].destination, match_second[i].weight, match_second[i].enabled, match_second[i].innovation); 121 | } 122 | } 123 | 124 | for (int i = 0; i < disjoint_first.Count; i++) 125 | { 126 | child.AddEdge(disjoint_first[i].source, disjoint_first[i].destination, disjoint_first[i].weight, disjoint_first[i].enabled, disjoint_first[i].innovation); 127 | } 128 | 129 | for (int i = 0; i < excess_first.Count; i++) 130 | { 131 | child.AddEdge(excess_first[i].source, excess_first[i].destination, excess_first[i].weight, excess_first[i].enabled, excess_first[i].innovation); 132 | } 133 | 134 | child.SortEdges(); 135 | 136 | List ends = new List(); 137 | 138 | int vertexCount = first.vertices.Count; 139 | 140 | for (int i = 0; i < first.vertices.Count; i++) 141 | { 142 | VertexInfo vertex = first.vertices[i]; 143 | 144 | if (vertex.type == VertexInfo.EType.HIDDEN) 145 | { 146 | break; 147 | } 148 | 149 | ends.Add(vertex.index); 150 | child.AddVertex(vertex.type, vertex.index); 151 | } 152 | 153 | AddUniqueVertices(child, ends); 154 | 155 | child.SortVertices(); 156 | 157 | return child; 158 | } 159 | 160 | public void AddUniqueVertices(Genotype genotype, List ends) 161 | { 162 | List unique = new List(); 163 | 164 | int edgeCount = genotype.edges.Count; 165 | 166 | for (int i = 0; i < edgeCount; i++) 167 | { 168 | EdgeInfo info = genotype.edges[i]; 169 | 170 | if (!ends.Contains(info.source) && !unique.Contains(info.source)) 171 | { 172 | unique.Add(info.source); 173 | } 174 | 175 | if (!ends.Contains(info.destination) && !unique.Contains(info.destination)) 176 | { 177 | unique.Add(info.destination); 178 | } 179 | } 180 | 181 | int uniques = unique.Count; 182 | 183 | for (int i = 0; i < uniques; i++) 184 | { 185 | genotype.AddVertex(VertexInfo.EType.HIDDEN, unique[i]); 186 | } 187 | } 188 | 189 | public float SpeciationDistance(Genotype first, Genotype second) 190 | { 191 | List copy_first = new List(); 192 | List copy_second = new List(); 193 | 194 | copy_first.AddRange(first.edges); 195 | copy_second.AddRange(second.edges); 196 | 197 | List match_first = new List(); 198 | List match_second = new List(); 199 | 200 | List disjoint_first = new List(); 201 | List disjoint_second = new List(); 202 | 203 | List excess_first = new List(); 204 | List excess_second = new List(); 205 | 206 | int genes_first = first.edges.Count; 207 | int genes_second = second.edges.Count; 208 | 209 | int invmax_first = first.edges[first.edges.Count - 1].innovation; 210 | int invmax_second = second.edges[second.edges.Count - 1].innovation; 211 | 212 | int invmin = invmax_first > invmax_second ? invmax_second : invmax_first; 213 | 214 | float diff = 0.0f; 215 | 216 | for (int i = 0; i < genes_first; i++) 217 | { 218 | for (int j = 0; j < genes_second; j++) 219 | { 220 | EdgeInfo info_first = copy_first[i]; 221 | EdgeInfo info_second = copy_second[j]; 222 | 223 | //matching genes 224 | if (info_first.innovation == info_second.innovation) 225 | { 226 | float weightDiff = Math.Abs(info_first.weight - info_second.weight); 227 | diff += weightDiff; 228 | 229 | match_first.Add(info_first); 230 | match_second.Add(info_second); 231 | 232 | copy_first.Remove(info_first); 233 | copy_second.Remove(info_second); 234 | 235 | i--; 236 | genes_first--; 237 | genes_second--; 238 | break; 239 | } 240 | } 241 | } 242 | 243 | for (int i = 0; i < copy_first.Count; i++) 244 | { 245 | if (copy_first[i].innovation > invmin) 246 | { 247 | excess_first.Add(copy_first[i]); 248 | } 249 | else 250 | { 251 | disjoint_first.Add(copy_first[i]); 252 | } 253 | } 254 | 255 | for (int i = 0; i < copy_second.Count; i++) 256 | { 257 | if (copy_second[i].innovation > invmin) 258 | { 259 | excess_second.Add(copy_second[i]); 260 | } 261 | else 262 | { 263 | disjoint_second.Add(copy_second[i]); 264 | } 265 | } 266 | 267 | int match = match_first.Count; 268 | int disjoint = disjoint_first.Count + disjoint_second.Count; 269 | int excess = excess_first.Count + excess_second.Count; 270 | 271 | int n = Math.Max(first.edges.Count, second.edges.Count); 272 | 273 | float E = excess / (float)n; 274 | float D = disjoint / (float)n; 275 | float W = diff / (float)match; 276 | 277 | return E * C1 + D * C2 + W * C3; 278 | } 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /Monopoly/NeuroEvolution/Genotype.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace NEAT 8 | { 9 | public class VertexInfo 10 | { 11 | public enum EType 12 | { 13 | INPUT = 0, 14 | HIDDEN = 1, 15 | OUTPUT = 2, 16 | } 17 | 18 | public EType type; 19 | public int index = 0; 20 | 21 | public VertexInfo(EType t, int i) 22 | { 23 | type = t; 24 | index = i; 25 | } 26 | } 27 | 28 | public class EdgeInfo 29 | { 30 | //structual information 31 | public int source = 0; 32 | public int destination = 0; 33 | 34 | //network information 35 | public float weight = 0.0f; 36 | public bool enabled = false; 37 | public int innovation = 0; 38 | 39 | public EdgeInfo(int s, int d, float w, bool e) 40 | { 41 | source = s; 42 | destination = d; 43 | 44 | weight = w; 45 | enabled = e; 46 | } 47 | } 48 | 49 | public class Genotype 50 | { 51 | public List vertices; 52 | public List edges; 53 | 54 | public int inputs = 0; 55 | public int externals = 0; 56 | 57 | public int bracket = 0; 58 | 59 | public float fitness = 0.0f; 60 | public float adjustedFitness = 0.0f; 61 | 62 | public Genotype() 63 | { 64 | vertices = new List(); 65 | edges = new List(); 66 | } 67 | 68 | public void AddVertex(VertexInfo.EType type, int index) 69 | { 70 | VertexInfo v = new VertexInfo(type, index); 71 | vertices.Add(v); 72 | 73 | if (v.type != VertexInfo.EType.HIDDEN) 74 | { 75 | externals++; 76 | } 77 | 78 | if (v.type == VertexInfo.EType.INPUT) 79 | { 80 | inputs++; 81 | } 82 | } 83 | 84 | public void AddEdge(int source, int destination, float weight, bool enabled) 85 | { 86 | EdgeInfo e = new EdgeInfo(source, destination, weight, enabled); 87 | edges.Add(e); 88 | } 89 | 90 | public void AddEdge(int source, int destination, float weight, bool enabled, int innovation) 91 | { 92 | EdgeInfo e = new EdgeInfo(source, destination, weight, enabled); 93 | e.innovation = innovation; 94 | edges.Add(e); 95 | } 96 | 97 | public Genotype Clone() 98 | { 99 | Genotype copy = new Genotype(); 100 | 101 | int vertexCount = vertices.Count; 102 | 103 | for (int i = 0; i < vertexCount; i++) 104 | { 105 | copy.AddVertex(vertices[i].type, vertices[i].index); 106 | } 107 | 108 | int edgeCount = edges.Count; 109 | 110 | for (int i = 0; i < edgeCount; i++) 111 | { 112 | copy.AddEdge(edges[i].source, edges[i].destination, edges[i].weight, edges[i].enabled, edges[i].innovation); 113 | } 114 | 115 | return copy; 116 | } 117 | 118 | public void SortTopology() 119 | { 120 | SortVertices(); 121 | SortEdges(); 122 | } 123 | 124 | public void SortVertices() 125 | { 126 | vertices.Sort(CompareVertexByOrder); 127 | } 128 | 129 | public void SortEdges() 130 | { 131 | edges.Sort(CompareEdgeByInnovation); 132 | } 133 | 134 | public int CompareVertexByOrder(VertexInfo a, VertexInfo b) 135 | { 136 | if (a.index > b.index) 137 | { 138 | return 1; 139 | } 140 | else if (a.index == b.index) 141 | { 142 | return 0; 143 | } 144 | 145 | return -1; 146 | } 147 | 148 | public int CompareEdgeByInnovation(EdgeInfo a, EdgeInfo b) 149 | { 150 | if (a.innovation > b.innovation) 151 | { 152 | return 1; 153 | } 154 | else if (a.innovation == b.innovation) 155 | { 156 | return 0; 157 | } 158 | 159 | return -1; 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Monopoly/NeuroEvolution/Mutation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace NEAT 8 | { 9 | public class Marking 10 | { 11 | public int order = 0; 12 | public int source = 0; 13 | public int destination = 0; 14 | } 15 | 16 | public class Mutation 17 | { 18 | public static Mutation instance = null; 19 | 20 | public float MUTATE_LINK = 0.2f; 21 | public float MUTATE_NODE = 0.1f; 22 | public float MUTATE_ENABLE = 0.6f; 23 | public float MUTATE_DISABLE = 0.2f; 24 | public float MUTATE_WEIGHT = 2.0f; 25 | 26 | //chances 27 | public float PETRUB_CHANCE = 0.9f; 28 | public float SHIFT_STEP = 0.1f; 29 | 30 | public List historical = new List(); 31 | 32 | public static void Initialise() 33 | { 34 | if (instance == null) 35 | { 36 | instance = new Mutation(); 37 | } 38 | } 39 | 40 | public Mutation() 41 | { 42 | 43 | } 44 | 45 | public int RegisterMarking(EdgeInfo info) 46 | { 47 | int count = historical.Count; 48 | 49 | for (int i = 0; i < count; i++) 50 | { 51 | Marking marking = historical[i]; 52 | 53 | if (marking.source == info.source && marking.destination == info.destination) 54 | { 55 | return marking.order; 56 | } 57 | } 58 | 59 | Marking creation = new Marking(); 60 | creation.order = historical.Count; 61 | creation.source = info.source; 62 | creation.destination = info.destination; 63 | 64 | historical.Add(creation); 65 | 66 | return historical.Count - 1; 67 | } 68 | 69 | public void MutateAll(Genotype genotype) 70 | { 71 | MUTATE_LINK = 0.2f; 72 | MUTATE_NODE = 0.1f; 73 | MUTATE_ENABLE = 0.6f; 74 | MUTATE_DISABLE = 0.2f; 75 | MUTATE_WEIGHT = 2.0f; 76 | 77 | float p = MUTATE_WEIGHT; 78 | 79 | while (p > 0) 80 | { 81 | float roll = (float)RNG.instance.gen.NextDouble(); 82 | 83 | if (roll < p) 84 | { 85 | MutateWeight(genotype); 86 | } 87 | 88 | p--; 89 | } 90 | 91 | p = MUTATE_LINK; 92 | 93 | while (p > 0) 94 | { 95 | float roll = (float)RNG.instance.gen.NextDouble(); 96 | 97 | if (roll < p) 98 | { 99 | MutateLink(genotype); 100 | } 101 | 102 | p--; 103 | } 104 | 105 | p = MUTATE_NODE; 106 | 107 | while (p > 0) 108 | { 109 | float roll = (float)RNG.instance.gen.NextDouble(); 110 | 111 | if (roll < p) 112 | { 113 | MutateNode(genotype); 114 | } 115 | 116 | p--; 117 | } 118 | 119 | p = MUTATE_DISABLE; 120 | 121 | while (p > 0) 122 | { 123 | float roll = (float)RNG.instance.gen.NextDouble(); 124 | 125 | if (roll < p) 126 | { 127 | MutateDisable(genotype); 128 | } 129 | 130 | p--; 131 | } 132 | 133 | p = MUTATE_ENABLE; 134 | 135 | while (p > 0) 136 | { 137 | float roll = (float)RNG.instance.gen.NextDouble(); 138 | 139 | if (roll < p) 140 | { 141 | MutateEnable(genotype); 142 | } 143 | 144 | p--; 145 | } 146 | 147 | } 148 | 149 | public void MutateLink(Genotype genotype) 150 | { 151 | int vertexCount = genotype.vertices.Count; 152 | int edgeCount = genotype.edges.Count; 153 | 154 | List potential = new List(); 155 | 156 | //gather all possible potential edges 157 | for (int i = 0; i < vertexCount; i++) 158 | { 159 | for (int j = 0; j < vertexCount; j++) 160 | { 161 | int source = genotype.vertices[i].index; 162 | int destination = genotype.vertices[j].index; 163 | 164 | VertexInfo.EType t1 = genotype.vertices[i].type; 165 | VertexInfo.EType t2 = genotype.vertices[j].type; 166 | 167 | if (t1 == VertexInfo.EType.OUTPUT || t2 == VertexInfo.EType.INPUT) 168 | { 169 | continue; 170 | } 171 | 172 | if (source == destination) 173 | { 174 | continue; 175 | } 176 | 177 | bool search = false; 178 | 179 | //match edge 180 | for (int k = 0; k < edgeCount; k++) 181 | { 182 | EdgeInfo edge = genotype.edges[k]; 183 | 184 | if (edge.source == source && edge.destination == destination) 185 | { 186 | search = true; 187 | break; 188 | } 189 | } 190 | 191 | if (!search) 192 | { 193 | float weight = (float)RNG.instance.gen.NextDouble() * 4.0f - 2.0f; 194 | EdgeInfo creation = new EdgeInfo(source, destination, weight, true); 195 | 196 | potential.Add(creation); 197 | } 198 | } 199 | } 200 | 201 | if (potential.Count <= 0) 202 | { 203 | return; 204 | } 205 | 206 | int selection = RNG.instance.gen.Next(0, potential.Count); 207 | 208 | EdgeInfo mutation = potential[selection]; 209 | mutation.innovation = RegisterMarking(mutation); 210 | 211 | genotype.AddEdge(mutation.source, mutation.destination, mutation.weight, mutation.enabled, mutation.innovation); 212 | } 213 | 214 | public void MutateNode(Genotype genotype) 215 | { 216 | int edgeCount = genotype.edges.Count; 217 | 218 | int selection = RNG.instance.gen.Next(0, edgeCount); 219 | 220 | EdgeInfo edge = genotype.edges[selection]; 221 | 222 | if (edge.enabled == false) 223 | { 224 | return; 225 | } 226 | 227 | edge.enabled = false; 228 | 229 | int vertex_new = genotype.vertices[genotype.vertices.Count - 1].index + 1; 230 | 231 | VertexInfo vertex = new VertexInfo(VertexInfo.EType.HIDDEN, vertex_new); 232 | 233 | EdgeInfo first = new EdgeInfo(edge.source, vertex_new, 1.0f, true); 234 | EdgeInfo second = new EdgeInfo(vertex_new, edge.destination, edge.weight, true); 235 | 236 | first.innovation = RegisterMarking(first); 237 | second.innovation = RegisterMarking(second); 238 | 239 | genotype.AddVertex(vertex.type, vertex.index); 240 | 241 | genotype.AddEdge(first.source, first.destination, first.weight, first.enabled, first.innovation); 242 | genotype.AddEdge(second.source, second.destination, second.weight, second.enabled, second.innovation); 243 | } 244 | 245 | public void MutateEnable(Genotype genotype) 246 | { 247 | int edgeCount = genotype.edges.Count; 248 | 249 | List candidates = new List(); 250 | 251 | for (int i =0; i < edgeCount; i++) 252 | { 253 | if (!genotype.edges[i].enabled) 254 | { 255 | candidates.Add(genotype.edges[i]); 256 | } 257 | } 258 | 259 | if (candidates.Count == 0) 260 | { 261 | return; 262 | } 263 | 264 | int selection = RNG.instance.gen.Next(0, candidates.Count); 265 | 266 | EdgeInfo edge = candidates[selection]; 267 | edge.enabled = true; 268 | } 269 | 270 | public void MutateDisable(Genotype genotype) 271 | { 272 | int edgeCount = genotype.edges.Count; 273 | 274 | List candidates = new List(); 275 | 276 | for (int i = 0; i < edgeCount; i++) 277 | { 278 | if (genotype.edges[i].enabled) 279 | { 280 | candidates.Add(genotype.edges[i]); 281 | } 282 | } 283 | 284 | if (candidates.Count == 0) 285 | { 286 | return; 287 | } 288 | 289 | int selection = RNG.instance.gen.Next(0, candidates.Count); 290 | 291 | EdgeInfo edge = candidates[selection]; 292 | edge.enabled = false; 293 | } 294 | 295 | public void MutateWeight(Genotype genotype) 296 | { 297 | int selection = RNG.instance.gen.Next(0, genotype.edges.Count); 298 | 299 | EdgeInfo edge = genotype.edges[selection]; 300 | 301 | float roll = (float)RNG.instance.gen.NextDouble(); 302 | 303 | if (roll < PETRUB_CHANCE) 304 | { 305 | MutateWeightShift(edge, SHIFT_STEP); 306 | } 307 | else 308 | { 309 | MutateWeightRandom(edge); 310 | } 311 | } 312 | 313 | public void MutateWeightShift(EdgeInfo edge, float step) 314 | { 315 | float scalar = (float)RNG.instance.gen.NextDouble() * step - step * 0.5f; 316 | edge.weight += scalar; 317 | } 318 | 319 | public void MutateWeightRandom(EdgeInfo edge) 320 | { 321 | float value = (float)RNG.instance.gen.NextDouble() * 4.0f - 2.0f; 322 | edge.weight = value; 323 | } 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /Monopoly/NeuroEvolution/NetworkFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace NEAT 8 | { 9 | public class NetworkFactory 10 | { 11 | public static NetworkFactory instance = null; 12 | 13 | public static void Initialise() 14 | { 15 | if (instance == null) 16 | { 17 | instance = new NetworkFactory(); 18 | } 19 | } 20 | 21 | public Genotype CreateBaseGenotype(int inputs, int outputs) 22 | { 23 | Genotype network = new Genotype(); 24 | 25 | for (int i = 0; i < inputs; i++) 26 | { 27 | network.AddVertex(VertexInfo.EType.INPUT, i); 28 | } 29 | 30 | for (int i = 0; i < outputs; i++) 31 | { 32 | network.AddVertex(VertexInfo.EType.OUTPUT, i + inputs); 33 | } 34 | 35 | network.AddEdge(0, inputs, 0.0f, true, 0); 36 | 37 | //int innovation = 0; 38 | // 39 | //for (int i = 0; i < inputs; i++) 40 | //{ 41 | // for (int j = 0; j < outputs; j++) 42 | // { 43 | // int input = i; 44 | // int output = j + inputs; 45 | // 46 | // network.AddEdge(input, output, 0.0f, true, innovation); 47 | // 48 | // innovation++; 49 | // } 50 | //} 51 | 52 | return network; 53 | } 54 | 55 | public void RegisterBaseMarkings(int inputs, int outputs) 56 | { 57 | for (int i = 0; i < inputs; i++) 58 | { 59 | for (int j = 0; j < outputs; j++) 60 | { 61 | int input = i; 62 | int output = j + inputs; 63 | 64 | EdgeInfo info = new EdgeInfo(input, output, 0.0f, true); 65 | 66 | Mutation.instance.RegisterMarking(info); 67 | } 68 | } 69 | } 70 | 71 | public Genotype CreateBaseRecurrent() 72 | { 73 | Genotype network = new Genotype(); 74 | 75 | int nodeNum = 0; 76 | 77 | for (int i = 0; i < 1; i++) 78 | { 79 | network.AddVertex(VertexInfo.EType.INPUT, nodeNum); 80 | nodeNum++; 81 | } 82 | 83 | for (int i = 0; i < 1; i++) 84 | { 85 | network.AddVertex(VertexInfo.EType.OUTPUT, nodeNum); 86 | nodeNum++; 87 | } 88 | 89 | network.AddEdge(0, 1, 0.0f, true, 0); 90 | network.AddEdge(1, 0, 0.0f, true, 1); 91 | 92 | Phenotype physicals = new Phenotype(); 93 | physicals.InscribeGenotype(network); 94 | physicals.ProcessGraph(); 95 | 96 | return network; 97 | } 98 | 99 | public Genotype CreateBuggyNetwork() 100 | { 101 | Genotype network = new Genotype(); 102 | 103 | int nodeNum = 0; 104 | 105 | for (int i = 0; i < 2; i++) 106 | { 107 | network.AddVertex(VertexInfo.EType.INPUT, nodeNum); 108 | nodeNum++; 109 | } 110 | 111 | for (int i = 0; i < 1; i++) 112 | { 113 | network.AddVertex(VertexInfo.EType.OUTPUT, nodeNum); 114 | nodeNum++; 115 | } 116 | 117 | for (int i = 0; i < 2; i++) 118 | { 119 | network.AddVertex(VertexInfo.EType.HIDDEN, nodeNum); 120 | nodeNum++; 121 | } 122 | 123 | network.AddEdge(0, 2, 0.0f, true, 0); 124 | network.AddEdge(1, 2, 0.0f, true, 1); 125 | network.AddEdge(1, 3, 0.0f, true, 2); 126 | network.AddEdge(3, 2, 0.0f, true, 3); 127 | 128 | Phenotype physicals = new Phenotype(); 129 | physicals.InscribeGenotype(network); 130 | physicals.ProcessGraph(); 131 | 132 | return network; 133 | } 134 | 135 | public Phenotype CreateBasePhenotype(int inputs, int outputs) 136 | { 137 | Phenotype network = new Phenotype(); 138 | 139 | for (int i = 0; i < inputs; i++) 140 | { 141 | network.AddVertex(Vertex.EType.INPUT, i); 142 | } 143 | 144 | for (int i = 0; i < outputs; i++) 145 | { 146 | network.AddVertex(Vertex.EType.OUTPUT, i + inputs); 147 | } 148 | 149 | for (int i = 0; i < inputs; i++) 150 | { 151 | for (int j = 0; j < outputs; j++) 152 | { 153 | int input = i; 154 | int output = j + inputs; 155 | 156 | network.AddEdge(input, output, 0.0f, true); 157 | } 158 | } 159 | 160 | return network; 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /Monopoly/NeuroEvolution/Phenotype.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace NEAT 8 | { 9 | public class Vertex 10 | { 11 | public enum EType 12 | { 13 | INPUT = 0, 14 | HIDDEN = 1, 15 | OUTPUT = 2, 16 | } 17 | 18 | public EType type; 19 | public int index = 0; 20 | 21 | //structual information 22 | public List incoming; 23 | 24 | //output extraction 25 | public float value = 0.0f; 26 | 27 | public Vertex(EType t, int i) 28 | { 29 | type = t; 30 | index = i; 31 | 32 | incoming = new List(); 33 | } 34 | } 35 | 36 | public class Edge 37 | { 38 | public enum EType 39 | { 40 | FORWARD, 41 | RECURRENT, 42 | } 43 | 44 | //structual information 45 | public EType type = EType.FORWARD; 46 | public int source = 0; 47 | public int destination = 0; 48 | 49 | //network information 50 | public float weight = 0.0f; 51 | public bool enabled = true; 52 | 53 | //propagation information 54 | public float signal = 0.0f; 55 | 56 | public Edge(int s, int d, float w, bool e) 57 | { 58 | source = s; 59 | destination = d; 60 | 61 | weight = w; 62 | enabled = e; 63 | } 64 | } 65 | 66 | public class Phenotype 67 | { 68 | public List vertices; 69 | public List edges; 70 | 71 | public List vertices_inputs; 72 | public List vertices_outputs; 73 | 74 | public float score = 0; 75 | 76 | public Phenotype() 77 | { 78 | vertices = new List(); 79 | edges = new List(); 80 | 81 | vertices_inputs = new List(); 82 | vertices_outputs = new List(); 83 | } 84 | 85 | public void InscribeGenotype(Genotype code) 86 | { 87 | vertices.Clear(); 88 | edges.Clear(); 89 | 90 | int vertexCount = code.vertices.Count; 91 | int edgeCount = code.edges.Count; 92 | 93 | for (int i = 0; i < vertexCount; i++) 94 | { 95 | //cast to int then to other enumerator type 96 | AddVertex((Vertex.EType)(int)code.vertices[i].type, code.vertices[i].index); 97 | } 98 | 99 | for (int i = 0; i < edgeCount; i++) 100 | { 101 | AddEdge(code.edges[i].source, code.edges[i].destination, code.edges[i].weight, code.edges[i].enabled); 102 | } 103 | } 104 | 105 | public void AddVertex(Vertex.EType type, int index) 106 | { 107 | Vertex v = new Vertex(type, index); 108 | vertices.Add(v); 109 | } 110 | 111 | public void AddEdge(int source, int destination, float weight, bool enabled) 112 | { 113 | Edge e = new Edge(source, destination, weight, enabled); 114 | edges.Add(e); 115 | 116 | vertices[e.destination].incoming.Add(e); 117 | } 118 | 119 | public void ProcessGraph() 120 | { 121 | int verticesCount = vertices.Count; 122 | 123 | //populate input and output sub-lists 124 | for (int i = 0; i < verticesCount; i++) 125 | { 126 | Vertex vertex = vertices[i]; 127 | 128 | if (vertex.type == Vertex.EType.INPUT) 129 | { 130 | vertices_inputs.Add(vertex); 131 | } 132 | else if (vertex.type == Vertex.EType.OUTPUT) 133 | { 134 | vertices_outputs.Add(vertex); 135 | } 136 | } 137 | } 138 | 139 | public void ResetGraph() 140 | { 141 | int verticesCount = vertices.Count; 142 | 143 | for (int i = 0; i < verticesCount; i++) 144 | { 145 | Vertex vertex = vertices[i]; 146 | vertex.value = 0.0f; 147 | } 148 | } 149 | 150 | public float[] Propagate(float[] X) 151 | { 152 | int repeats = 10; 153 | 154 | for (int e = 0; e < repeats; e++) 155 | { 156 | for (int i = 0; i < vertices_inputs.Count; i++) 157 | { 158 | vertices_inputs[i].value = X[i]; 159 | } 160 | 161 | for (int i = 0; i < vertices.Count; i++) 162 | { 163 | if (vertices[i].type == Vertex.EType.OUTPUT) 164 | { 165 | continue; 166 | } 167 | 168 | int paths = vertices[i].incoming.Count; 169 | 170 | for (int j = 0; j < paths; j++) 171 | { 172 | vertices[i].value += vertices[vertices[i].incoming[j].source].value * vertices[i].incoming[j].weight * (vertices[i].incoming[j].enabled ? 1.0f : 0.0f); 173 | } 174 | 175 | if (vertices[i].incoming.Count > 0) 176 | { 177 | vertices[i].value = Sigmoid(vertices[i].value); 178 | } 179 | } 180 | 181 | float[] Y = new float[vertices_outputs.Count]; 182 | 183 | for (int i = 0; i < vertices_outputs.Count; i++) 184 | { 185 | int paths = vertices_outputs[i].incoming.Count; 186 | 187 | for (int j = 0; j < paths; j++) 188 | { 189 | vertices_outputs[i].value += vertices[vertices_outputs[i].incoming[j].source].value * vertices_outputs[i].incoming[j].weight * (vertices_outputs[i].incoming[j].enabled ? 1.0f : 0.0f); 190 | } 191 | 192 | if (vertices_outputs[i].incoming.Count > 0) 193 | { 194 | vertices_outputs[i].value = Sigmoid(vertices_outputs[i].value); 195 | Y[i] = vertices_outputs[i].value; 196 | } 197 | } 198 | 199 | if (e == repeats - 1) 200 | { 201 | return Y; 202 | } 203 | } 204 | 205 | return new float[0]; 206 | } 207 | 208 | public float Sigmoid(float x) 209 | { 210 | return 1.0f / (1.0f + (float)Math.Pow(Math.E, -1.0f * x)); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Monopoly/NeuroEvolution/Population.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace NEAT 8 | { 9 | public class Species 10 | { 11 | public List members; 12 | 13 | public float topFitness = 0.0f; 14 | public int staleness = 0; 15 | 16 | public float fitnessSum = 0.0f; 17 | 18 | public Species() 19 | { 20 | members = new List(); 21 | } 22 | 23 | public Genotype Breed() 24 | { 25 | float roll = (float)RNG.instance.gen.NextDouble(); 26 | 27 | if (roll < Crossover.instance.CROSSOVER_CHANCE && members.Count > 1) 28 | { 29 | int s1 = RNG.instance.gen.Next(0, members.Count); 30 | int s2 = RNG.instance.gen.Next(0, members.Count - 1); 31 | 32 | if (s2 >= s1) 33 | { 34 | s2++; 35 | } 36 | 37 | if (s1 > s2) 38 | { 39 | int temp = s1; 40 | s1 = s2; 41 | s2 = temp; 42 | } 43 | 44 | Genotype child = Crossover.instance.ProduceOffspring(members[s1], members[s2]); 45 | Mutation.instance.MutateAll(child); 46 | 47 | int selection = RNG.instance.gen.Next(0, members.Count); 48 | 49 | return child; 50 | } 51 | else 52 | { 53 | int selection = RNG.instance.gen.Next(0, members.Count); 54 | 55 | Genotype child = members[selection].Clone(); 56 | Mutation.instance.MutateAll(child); 57 | 58 | return child; 59 | } 60 | } 61 | 62 | public void SortMembers() 63 | { 64 | members.Sort(SortGenotypeByFitness); 65 | } 66 | 67 | public int SortGenotypeByFitness(Genotype a, Genotype b) 68 | { 69 | if (a.adjustedFitness > b.adjustedFitness) 70 | { 71 | return -1; 72 | } 73 | else if (a.adjustedFitness == b.adjustedFitness) 74 | { 75 | return 0; 76 | } 77 | 78 | return 1; 79 | } 80 | 81 | public void CullToPortion(float portion) 82 | { 83 | if (members.Count <= 1) 84 | { 85 | return; 86 | } 87 | 88 | int remaining = (int)Math.Ceiling(members.Count * portion); 89 | members.RemoveRange(remaining, members.Count - remaining); 90 | } 91 | 92 | public void CullToOne() 93 | { 94 | if (members.Count <= 1) 95 | { 96 | return; 97 | } 98 | 99 | members.RemoveRange(1, members.Count - 1); 100 | } 101 | 102 | public void CalculateAdjustedFitnessSum() 103 | { 104 | float sum = 0.0f; 105 | int membersCount = members.Count; 106 | 107 | for (int i = 0; i < membersCount; i++) 108 | { 109 | sum += members[i].adjustedFitness; 110 | } 111 | 112 | fitnessSum = sum; 113 | } 114 | } 115 | 116 | public class Population 117 | { 118 | public static Population instance = null; 119 | 120 | public int GENERATION = 0; 121 | 122 | public int POPULATION_SIZE = 256; 123 | public int INPUTS = 126; 124 | public int OUTPUTS = 7; 125 | public int MAX_STALENESS = 15; 126 | 127 | public float PORTION = 0.2f; 128 | 129 | public List species; 130 | public List genetics; 131 | public List population; 132 | 133 | public static void Initialise() 134 | { 135 | if (instance == null) 136 | { 137 | instance = new Population(); 138 | } 139 | } 140 | 141 | public Population() 142 | { 143 | species = new List(); 144 | genetics = new List(); 145 | population = new List(); 146 | } 147 | 148 | public void GenerateBasePopulation(int size, int inputs, int outputs) 149 | { 150 | POPULATION_SIZE = size; 151 | INPUTS = inputs; 152 | OUTPUTS = outputs; 153 | 154 | for (int i = 0; i < POPULATION_SIZE; i++) 155 | { 156 | Genotype genotype = NetworkFactory.instance.CreateBaseGenotype(inputs, outputs); 157 | genetics.Add(genotype); 158 | 159 | AddToSpecies(genotype); 160 | } 161 | 162 | NetworkFactory.instance.RegisterBaseMarkings(inputs, outputs); 163 | 164 | for (int i = 0; i < POPULATION_SIZE; i++) 165 | { 166 | Mutation.instance.MutateAll(genetics[i]); 167 | } 168 | 169 | InscribePopulation(); 170 | } 171 | 172 | public void NewGeneration() 173 | { 174 | CalculateAdjustedFitness(); 175 | 176 | for (int i = 0; i < species.Count; i++) 177 | { 178 | species[i].SortMembers(); 179 | species[i].CullToPortion(PORTION); 180 | 181 | if (species[i].members.Count <= 1) 182 | { 183 | species.RemoveAt(i); 184 | i--; 185 | } 186 | } 187 | 188 | UpdateStaleness(); 189 | 190 | float fitnessSum = 0.0f; 191 | 192 | for (int i = 0; i < species.Count; i++) 193 | { 194 | species[i].CalculateAdjustedFitnessSum(); 195 | fitnessSum += species[i].fitnessSum; 196 | } 197 | 198 | List children = new List(); 199 | 200 | for (int i = 0; i < species.Count; i++) 201 | { 202 | int build = (int)(POPULATION_SIZE * (species[i].fitnessSum / fitnessSum)) - 1; 203 | 204 | for (int j = 0; j < build; j++) 205 | { 206 | Genotype child = species[i].Breed(); 207 | children.Add(child); 208 | } 209 | 210 | } 211 | 212 | while (POPULATION_SIZE > species.Count + children.Count) 213 | { 214 | Genotype child = species[RNG.instance.gen.Next(0,species.Count)].Breed(); 215 | children.Add(child); 216 | } 217 | 218 | for (int i = 0; i < species.Count; i++) 219 | { 220 | species[i].CullToOne(); 221 | } 222 | 223 | int childrenCount = children.Count; 224 | 225 | for (int i = 0; i < childrenCount; i++) 226 | { 227 | AddToSpecies(children[i]); 228 | } 229 | 230 | genetics.Clear(); 231 | 232 | for (int i = 0; i < species.Count; i++) 233 | { 234 | for (int j = 0; j < species[i].members.Count; j++) 235 | { 236 | genetics.Add(species[i].members[j]); 237 | } 238 | } 239 | 240 | InscribePopulation(); 241 | 242 | GENERATION++; 243 | } 244 | 245 | public void CalculateAdjustedFitness() 246 | { 247 | int speciesCount = species.Count; 248 | 249 | for (int i = 0; i < speciesCount; i++) 250 | { 251 | int membersCount = species[i].members.Count; 252 | 253 | for (int j = 0; j < membersCount; j++) 254 | { 255 | species[i].members[j].adjustedFitness = species[i].members[j].fitness / membersCount; 256 | } 257 | } 258 | } 259 | 260 | public void UpdateStaleness() 261 | { 262 | int speciesCount = species.Count; 263 | 264 | for (int i = 0; i < speciesCount; i++) 265 | { 266 | if (speciesCount == 1) 267 | { 268 | return; 269 | } 270 | 271 | float top = species[i].members[0].fitness; 272 | 273 | if (species[i].topFitness < top) 274 | { 275 | species[i].topFitness = top; 276 | species[i].staleness = 0; 277 | } 278 | else 279 | { 280 | species[i].staleness++; 281 | } 282 | 283 | if (species[i].staleness >= MAX_STALENESS) 284 | { 285 | species.RemoveAt(i); 286 | i--; 287 | speciesCount--; 288 | } 289 | } 290 | } 291 | 292 | public void InscribePopulation() 293 | { 294 | population.Clear(); 295 | 296 | for (int i = 0; i < POPULATION_SIZE; i++) 297 | { 298 | genetics[i].fitness = 0.0f; 299 | genetics[i].adjustedFitness = 0.0f; 300 | 301 | Phenotype physical = new Phenotype(); 302 | physical.InscribeGenotype(genetics[i]); 303 | physical.ProcessGraph(); 304 | 305 | population.Add(physical); 306 | } 307 | } 308 | 309 | public void AddToSpecies(Genotype genotype) 310 | { 311 | if (species.Count == 0) 312 | { 313 | Species new_species = new Species(); 314 | new_species.members.Add(genotype); 315 | 316 | species.Add(new_species); 317 | } 318 | else 319 | { 320 | int speciesCount = species.Count; 321 | 322 | bool found = false; 323 | 324 | for (int i = 0; i < speciesCount; i++) 325 | { 326 | float distance = Crossover.instance.SpeciationDistance(species[i].members[0], genotype); 327 | 328 | if (distance < Crossover.instance.DISTANCE) 329 | { 330 | species[i].members.Add(genotype); 331 | found = true; 332 | break; 333 | } 334 | } 335 | 336 | if (!found) 337 | { 338 | Species new_species = new Species(); 339 | new_species.members.Add(genotype); 340 | 341 | species.Add(new_species); 342 | } 343 | } 344 | } 345 | public int SortGenotypeByFitness(Genotype a, Genotype b) 346 | { 347 | if (a.fitness > b.fitness) 348 | { 349 | return -1; 350 | } 351 | else if (a.fitness == b.fitness) 352 | { 353 | return 0; 354 | } 355 | 356 | return 1; 357 | } 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /Monopoly/NeuroEvolution/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace NEAT 8 | { 9 | class OldProgram 10 | { 11 | //ic void OLD_MAIN() 12 | // 13 | //NetworkFactory.Initialise(); 14 | // 15 | //Mutation.Initialise(); 16 | //Crossover.Initialise(); 17 | //Population.Initialise(); 18 | // 19 | //Genotype buggy = NetworkFactory.instance.CreateBuggyNetwork(); 20 | // 21 | //Phenotype buggy_p = new Phenotype(); 22 | //buggy_p.InscribeGenotype(buggy); 23 | //buggy_p.ProcessGraph(); 24 | // 25 | //buggy_p.Propagate(new float[2] { 1.0f, 1.0f }); 26 | // 27 | //int POPULATION_SIZE = 100; 28 | //int INPUTS = 2; 29 | //int OUTPUTS = 1; 30 | // 31 | //int ATTEMPTS = 10; 32 | // 33 | //float[] X = new float[8] { 0, 0, 0, 1, 1, 0, 1, 1 }; 34 | //float[] Y = new float[4] { 0, 1, 1, 0 }; 35 | // 36 | //Population.instance.GenerateBasePopulation(POPULATION_SIZE, INPUTS, OUTPUTS); 37 | // 38 | //for (int g = 0; g < 50000; g++) 39 | //{ 40 | // for (int i = 0; i < POPULATION_SIZE; i++) 41 | // { 42 | // float totalError = 0.0f; 43 | // 44 | // for (int tries = 10; tries >= 0; tries--) 45 | // { 46 | // List selections = new List { 0, 1, 2, 3 }; 47 | // 48 | // for (int x = 0; x < 4; x++) 49 | // { 50 | // int index = Mutation.instance.rng.Next(0, selections.Count); 51 | // int select = selections[index]; 52 | // selections.RemoveAt(index); 53 | // 54 | // float[] Yh = Population.instance.population[i].Propagate(new float[2] { X[select * 2], X[select * 2 + 1] }); 55 | // 56 | // float error = Math.Abs(Y[select] - Yh[0]); 57 | // error *= error; 58 | // 59 | // totalError += error; 60 | // } 61 | // } 62 | // 63 | // float fitness = (ATTEMPTS * 4 - totalError); 64 | // 65 | // if (fitness > ATTEMPTS * 4 - 0.01f) 66 | // { 67 | // int breakpoints = 0; 68 | // 69 | // Population.instance.population[i].ResetGraph(); 70 | // 71 | // for (int a = ATTEMPTS; a >= 0; a--) 72 | // { 73 | // List selections = new List { 0, 1, 2, 3 }; 74 | // 75 | // for (int x = 0; x < 4; x++) 76 | // { 77 | // int index = Mutation.instance.rng.Next(0, selections.Count); 78 | // int select = selections[index]; 79 | // selections.RemoveAt(index); 80 | // 81 | // float[] Yh = Population.instance.population[i].Propagate(new float[2] { X[select * 2], X[select * 2 + 1] }); 82 | // 83 | // float error = Math.Abs(Y[select] - Yh[0]); 84 | // error *= error; 85 | // 86 | // totalError += error; 87 | // } 88 | // } 89 | // } 90 | // 91 | // Console.WriteLine(fitness); 92 | // 93 | // Population.instance.genetics[i].fitness = fitness; 94 | // } 95 | // 96 | // Population.instance.NewGeneration(); 97 | //} 98 | //} 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Monopoly/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace Monopoly 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | Analytics a = new Analytics(); 12 | Analytics.instance = a; 13 | 14 | string path = "C:\\Users\\Brad\\Desktop\\monopoly_population.txt"; 15 | 16 | RNG.Initialise(); 17 | 18 | NEAT.NetworkFactory.Initialise(); 19 | 20 | NEAT.Mutation.Initialise(); 21 | NEAT.Crossover.Initialise(); 22 | NEAT.Population.Initialise(); 23 | 24 | Tournament tournament = new Tournament(); 25 | 26 | //SaveState(path, tournament); 27 | 28 | if (File.Exists(path)) 29 | { 30 | //tournament.Initialise(); 31 | LoadState(path, ref tournament); 32 | } 33 | else 34 | { 35 | tournament.Initialise(); 36 | } 37 | 38 | for (int i = 0; i < 1000; i++) 39 | { 40 | tournament.ExecuteTournament(); 41 | NEAT.Population.instance.NewGeneration(); 42 | SaveState(path, tournament); 43 | } 44 | 45 | } 46 | 47 | public static char DELIM_MAIN = ';'; 48 | public static char DELIM_COMMA = ','; 49 | 50 | public static void SaveState(string target, Tournament tournament) 51 | { 52 | Console.WriteLine("SAVING POPULATION"); 53 | 54 | string build = ""; 55 | string build2 = ""; 56 | 57 | build += NEAT.Population.instance.GENERATION.ToString(); 58 | build += DELIM_MAIN; 59 | build += tournament.championScore.ToString(); 60 | build += DELIM_MAIN; 61 | 62 | int markings = 0; 63 | 64 | //save markings 65 | for (int i = 0; i < NEAT.Mutation.instance.historical.Count; i++) 66 | { 67 | build += NEAT.Mutation.instance.historical[i].order; 68 | build += DELIM_COMMA; 69 | 70 | build += NEAT.Mutation.instance.historical[i].source; 71 | build += DELIM_COMMA; 72 | 73 | build += NEAT.Mutation.instance.historical[i].destination; 74 | 75 | if (i != NEAT.Mutation.instance.historical.Count - 1) 76 | { 77 | build += DELIM_COMMA; 78 | } 79 | 80 | markings++; 81 | } 82 | 83 | List net_build = new List(); 84 | int net_count = -1; 85 | int gene_count = 0; 86 | 87 | build += DELIM_MAIN; 88 | 89 | //save neworks, species by species 90 | for (int i = 0; i < NEAT.Population.instance.species.Count; i++) 91 | { 92 | net_build.Add(""); 93 | net_count++; 94 | 95 | net_build[net_count] += NEAT.Population.instance.species[i].topFitness.ToString(); 96 | net_build[net_count] += DELIM_COMMA; 97 | net_build[net_count] += NEAT.Population.instance.species[i].staleness.ToString(); 98 | 99 | net_build[net_count] += "&"; 100 | 101 | int members = NEAT.Population.instance.species[i].members.Count; 102 | 103 | for (int j = 0; j < members; j++) 104 | { 105 | net_build.Add(""); 106 | net_count++; 107 | gene_count++; 108 | 109 | Console.WriteLine(gene_count + "/" + NEAT.Population.instance.genetics.Count); 110 | 111 | NEAT.Genotype genes = NEAT.Population.instance.species[i].members[j]; 112 | 113 | int vertices = genes.vertices.Count; 114 | 115 | for (int k = 0; k < vertices; k++) 116 | { 117 | net_build[net_count] += genes.vertices[k].index.ToString(); 118 | net_build[net_count] += DELIM_COMMA; 119 | net_build[net_count] += genes.vertices[k].type.ToString(); 120 | net_build[net_count] += DELIM_COMMA; 121 | } 122 | 123 | net_build[net_count] += '#'; 124 | 125 | int edges = genes.edges.Count; 126 | 127 | for (int k = 0; k < edges; k++) 128 | { 129 | net_build[net_count] += genes.edges[k].source.ToString(); 130 | net_build[net_count] += DELIM_COMMA; 131 | net_build[net_count] += genes.edges[k].destination.ToString(); 132 | net_build[net_count] += DELIM_COMMA; 133 | net_build[net_count] += genes.edges[k].weight.ToString(); 134 | net_build[net_count] += DELIM_COMMA; 135 | net_build[net_count] += genes.edges[k].enabled.ToString(); 136 | net_build[net_count] += DELIM_COMMA; 137 | net_build[net_count] += genes.edges[k].innovation.ToString(); 138 | net_build[net_count] += DELIM_COMMA; 139 | } 140 | 141 | if (j != members - 1) 142 | { 143 | net_build[net_count] += "n"; 144 | } 145 | } 146 | 147 | if (i != NEAT.Population.instance.species.Count - 1) 148 | { 149 | net_build[net_count] += "&"; 150 | } 151 | } 152 | 153 | build2 += DELIM_MAIN; 154 | 155 | using (StreamWriter sw = new StreamWriter(target)) 156 | { 157 | sw.Write(build); 158 | 159 | foreach (string b in net_build) 160 | { 161 | sw.Write(b); 162 | } 163 | 164 | sw.Write(build2); 165 | } 166 | 167 | Console.WriteLine(markings + " MARKINGS"); 168 | } 169 | 170 | public static void LoadState(string location, ref Tournament tournament) 171 | { 172 | string load = ""; 173 | 174 | using (StreamReader sr = new StreamReader(location)) 175 | { 176 | load = sr.ReadToEnd(); 177 | } 178 | 179 | string[] parts = load.Split(DELIM_MAIN); 180 | 181 | int gen = int.Parse(parts[0]); 182 | float score = float.Parse(parts[1]); 183 | 184 | NEAT.Population.instance.GENERATION = gen; 185 | tournament.championScore = score; 186 | 187 | string markingString = parts[2]; 188 | string[] markingParts = markingString.Split(DELIM_COMMA); 189 | 190 | for (int i = 0; i < markingParts.GetLength(0); i += 3) 191 | { 192 | int order = int.Parse(markingParts[i]); 193 | int source = int.Parse(markingParts[i + 1]); 194 | int destination = int.Parse(markingParts[i + 2]); 195 | 196 | NEAT.Marking recreation = new NEAT.Marking(); 197 | 198 | recreation.order = order; 199 | recreation.source = source; 200 | recreation.destination = destination; 201 | 202 | NEAT.Mutation.instance.historical.Add(recreation); 203 | } 204 | 205 | string networkString = parts[3]; 206 | string[] speciesParts = networkString.Split('&'); 207 | 208 | for (int x = 0; x < speciesParts.GetLength(0); x+=2) 209 | { 210 | string[] firstParts = speciesParts[x].Split(DELIM_COMMA); 211 | 212 | NEAT.Population.instance.species.Add(new NEAT.Species()); 213 | NEAT.Population.instance.species[NEAT.Population.instance.species.Count - 1].topFitness = float.Parse(firstParts[0]); 214 | NEAT.Population.instance.species[NEAT.Population.instance.species.Count - 1].staleness = int.Parse(firstParts[1]); 215 | 216 | string[] networkParts = speciesParts[x+1].Split('n'); 217 | 218 | for (int i = 0; i < networkParts.GetLength(0); i++) 219 | { 220 | NEAT.Genotype genotype = new NEAT.Genotype(); 221 | 222 | string network = networkParts[i]; 223 | string[] nparts = network.Split('#'); 224 | 225 | string verts = nparts[0]; 226 | string[] vparts = verts.Split(','); 227 | 228 | for (int j = 0; j < vparts.GetLength(0) - 1; j += 2) 229 | { 230 | int index = int.Parse(vparts[j]); 231 | NEAT.VertexInfo.EType type = (NEAT.VertexInfo.EType)Enum.Parse(typeof(NEAT.VertexInfo.EType), vparts[j + 1]); 232 | 233 | genotype.AddVertex(type, index); 234 | } 235 | 236 | string edges = nparts[1]; 237 | string[] eparts = edges.Split(','); 238 | 239 | for (int j = 0; j < eparts.GetLength(0) - 1; j += 5) 240 | { 241 | int source = int.Parse(eparts[j]); 242 | int destination = int.Parse(eparts[j + 1]); 243 | float weight = float.Parse(eparts[j + 2]); 244 | bool enabled = bool.Parse(eparts[j + 3]); 245 | int innovation = int.Parse(eparts[j + 4]); 246 | 247 | genotype.AddEdge(source, destination, weight, enabled, innovation); 248 | } 249 | 250 | NEAT.Population.instance.species[NEAT.Population.instance.species.Count - 1].members.Add(genotype); 251 | NEAT.Population.instance.genetics.Add(genotype); 252 | } 253 | } 254 | 255 | NEAT.Population.instance.InscribePopulation(); 256 | } 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /Monopoly/RNG.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | public class RNG 7 | { 8 | public static RNG instance; 9 | public Random gen; 10 | 11 | public RNG() 12 | { 13 | gen = new Random(); 14 | } 15 | 16 | public static void Initialise() 17 | { 18 | if (instance == null) 19 | { 20 | instance = new RNG(); 21 | } 22 | } 23 | 24 | public List Shuffle(List cards) 25 | { 26 | List shuffle = new List(); 27 | 28 | for (int i = 0; i < cards.Count;) 29 | { 30 | int r = gen.Next(0, cards.Count); 31 | shuffle.Add(cards[r]); 32 | cards.RemoveAt(r); 33 | } 34 | 35 | return shuffle; 36 | } 37 | 38 | public MONOPOLY.NeuralPlayer[] Shuffle(MONOPOLY.NeuralPlayer[] list) 39 | { 40 | List container = new List(list); 41 | List shuffle = new List(); 42 | 43 | for (int i = 0; i < container.Count;) 44 | { 45 | int r = gen.Next(0, container.Count); 46 | shuffle.Add(container[r]); 47 | container.RemoveAt(r); 48 | } 49 | 50 | return shuffle.ToArray(); 51 | } 52 | 53 | public List Shuffle(List list) 54 | { 55 | List shuffle = new List(); 56 | 57 | for (int i = 0; i < list.Count;) 58 | { 59 | int r = gen.Next(0, list.Count); 60 | shuffle.Add(list[r]); 61 | list.RemoveAt(r); 62 | } 63 | 64 | return shuffle; 65 | } 66 | 67 | public void DoubleShuffle(List phen, List gene, ref List op, ref List og) 68 | { 69 | for (int i = 0; i < phen.Count;) 70 | { 71 | int r = gen.Next(0, phen.Count); 72 | op.Add(phen[r]); 73 | og.Add(gene[r]); 74 | phen.RemoveAt(r); 75 | gene.RemoveAt(r); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /Monopoly/Tournament.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Monopoly 9 | { 10 | public class Tournament 11 | { 12 | public static int TOURNAMENT_SIZE = 256; 13 | public static int ROUND_SIZE = 2000; //2000 14 | 15 | public static int WORKERS = 20; //20 16 | public static int BATCH_SIZE = 20; //20 17 | 18 | public NEAT.Genotype champion = null; 19 | public float championScore = 0.0f; 20 | 21 | public List contestants; 22 | public List contestants_g; 23 | 24 | public Tournament() 25 | { 26 | contestants = new List(); 27 | contestants_g = new List(); 28 | } 29 | 30 | public void Initialise() 31 | { 32 | int INPUTS = 126; 33 | int OUTPUTS = 9; 34 | 35 | NEAT.Population.instance.GenerateBasePopulation(TOURNAMENT_SIZE, INPUTS, OUTPUTS); 36 | } 37 | 38 | public void ExecuteTournament() 39 | { 40 | Console.WriteLine("TOURNAMENT #" + NEAT.Population.instance.GENERATION); 41 | 42 | contestants.Clear(); 43 | contestants_g.Clear(); 44 | 45 | for (int i = 0; i < TOURNAMENT_SIZE; i++) 46 | { 47 | NEAT.Population.instance.genetics[i].bracket = 0; 48 | NEAT.Population.instance.population[i].score = 0.0f; 49 | 50 | contestants.Add(NEAT.Population.instance.population[i]); 51 | contestants_g.Add(NEAT.Population.instance.genetics[i]); 52 | } 53 | 54 | while (contestants.Count > 1) 55 | { 56 | ExecuteTournamentRound(); 57 | } 58 | 59 | for (int i = 0; i < TOURNAMENT_SIZE; i++) 60 | { 61 | float top = 0.0f; 62 | 63 | if (champion != null) 64 | { 65 | top = champion.bracket; 66 | } 67 | 68 | float diff = NEAT.Population.instance.genetics[i].bracket - top; 69 | NEAT.Population.instance.genetics[i].fitness = championScore + diff * 5; 70 | } 71 | 72 | champion = contestants_g[0]; 73 | championScore = contestants_g[0].fitness; 74 | } 75 | 76 | public void ExecuteTournamentRound() 77 | { 78 | 79 | Console.WriteLine("ROUND SIZE " + contestants.Count.ToString()); 80 | 81 | List cs = new List(); 82 | List cs_g = new List(); 83 | 84 | RNG.instance.DoubleShuffle(contestants, contestants_g, ref cs, ref cs_g); 85 | 86 | for (int i = 0; i < TOURNAMENT_SIZE; i++) 87 | { 88 | NEAT.Population.instance.population[i].score = 0.0f; 89 | } 90 | 91 | contestants = cs; 92 | contestants_g = cs_g; 93 | 94 | for (int i = 0; i < contestants.Count; i += 4) 95 | { 96 | int played = 0; 97 | 98 | Console.WriteLine("BRACKET (" + ( i / 4 ) + ")"); 99 | 100 | while (played < ROUND_SIZE) 101 | { 102 | Console.WriteLine("Initialised Workers"); 103 | 104 | Thread[] workers = new Thread[WORKERS]; 105 | 106 | for (int t = 0; t < WORKERS; t++) 107 | { 108 | workers[t] = new Thread(() => PlayGameThread(this, i)); 109 | workers[t].Start(); 110 | } 111 | 112 | for (int t = 0; t < WORKERS; t++) 113 | { 114 | workers[t].Join(); 115 | } 116 | 117 | played += WORKERS * BATCH_SIZE; 118 | 119 | for (int c = 0; c < 40; c++) 120 | { 121 | Console.WriteLine("index: " + c.ToString() + ", " + String.Format("{0:0.000}", Monopoly.Analytics.instance.ratio[c])); 122 | } 123 | } 124 | 125 | int mi = 0; 126 | float ms = contestants[i].score; 127 | 128 | for (int j = 1; j < 4; j++) 129 | { 130 | if (ms < contestants[i + j].score) 131 | { 132 | mi = j; 133 | ms = contestants[i + j].score; 134 | } 135 | } 136 | 137 | for (int j = 0; j < 4; j++) 138 | { 139 | if (j == mi) 140 | { 141 | contestants_g[i + j].bracket++; 142 | continue; 143 | } 144 | 145 | contestants[i + j] = null; 146 | } 147 | } 148 | 149 | for (int i = 0; i < contestants.Count; i++) 150 | { 151 | if (contestants[i] == null) 152 | { 153 | contestants.RemoveAt(i); 154 | contestants_g.RemoveAt(i); 155 | i--; 156 | } 157 | } 158 | 159 | return; 160 | } 161 | 162 | public static void PlayGameThread(Tournament instance, int i) 163 | { 164 | for (int game = 0; game < BATCH_SIZE; game++) 165 | { 166 | NetworkAdapter adapter = new NetworkAdapter(); 167 | MONOPOLY.Board board = new MONOPOLY.Board(adapter); 168 | 169 | board.players[0].network = instance.contestants[i]; 170 | board.players[1].network = instance.contestants[i + 1]; 171 | board.players[2].network = instance.contestants[i + 2]; 172 | board.players[3].network = instance.contestants[i + 3]; 173 | 174 | board.players[0].adapter = adapter; 175 | board.players[1].adapter = adapter; 176 | board.players[2].adapter = adapter; 177 | board.players[3].adapter = adapter; 178 | 179 | board.players = RNG.instance.Shuffle(board.players); 180 | 181 | MONOPOLY.Board.EOutcome outcome = MONOPOLY.Board.EOutcome.ONGOING; 182 | 183 | while (outcome == MONOPOLY.Board.EOutcome.ONGOING) 184 | { 185 | outcome = board.Step(); 186 | } 187 | 188 | if (outcome == MONOPOLY.Board.EOutcome.WIN1) 189 | { 190 | lock (board.players[0].network) 191 | { 192 | board.players[0].network.score += 1.0f; 193 | } 194 | 195 | for (int b = 0; b < board.players[0].items.Count; b++) 196 | { 197 | lock (Monopoly.Analytics.instance.wins) 198 | { 199 | Monopoly.Analytics.instance.MarkWin(board.players[0].items[b]); 200 | } 201 | } 202 | 203 | } 204 | else if (outcome == MONOPOLY.Board.EOutcome.WIN2) 205 | { 206 | lock (board.players[1].network) 207 | { 208 | board.players[1].network.score += 1.0f; 209 | } 210 | 211 | for (int b = 0; b < board.players[1].items.Count; b++) 212 | { 213 | lock (Monopoly.Analytics.instance.wins) 214 | { 215 | Monopoly.Analytics.instance.MarkWin(board.players[1].items[b]); 216 | } 217 | } 218 | } 219 | else if (outcome == MONOPOLY.Board.EOutcome.WIN3) 220 | { 221 | lock (board.players[2].network) 222 | { 223 | board.players[2].network.score += 1.0f; 224 | } 225 | 226 | for (int b = 0; b < board.players[2].items.Count; b++) 227 | { 228 | lock (Monopoly.Analytics.instance.wins) 229 | { 230 | Monopoly.Analytics.instance.MarkWin(board.players[2].items[b]); 231 | } 232 | } 233 | } 234 | else if (outcome == MONOPOLY.Board.EOutcome.WIN4) 235 | { 236 | lock (board.players[3].network) 237 | { 238 | board.players[3].network.score += 1.0f; 239 | } 240 | 241 | for (int b = 0; b < board.players[3].items.Count; b++) 242 | { 243 | lock (Monopoly.Analytics.instance.wins) 244 | { 245 | Monopoly.Analytics.instance.MarkWin(board.players[3].items[b]); 246 | } 247 | } 248 | } 249 | else if (outcome == MONOPOLY.Board.EOutcome.DRAW) 250 | { 251 | lock (board.players) 252 | { 253 | board.players[0].network.score += 0.25f; 254 | board.players[1].network.score += 0.25f; 255 | board.players[2].network.score += 0.25f; 256 | board.players[3].network.score += 0.25f; 257 | } 258 | } 259 | } 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | 2 | SETUP 3 | --------------------------- 4 | 5 | In Program.cs, change the path of the .txt file to match where you put your monopoly_population.txt file 6 | 7 | This algorithm runs several thousand games against every group in it's tournament to eliminate randomnness and allow good stratergies to be thoroughly tested. 8 | I made the program multi-threaded to make it play the games as fast as possible and this will most definitely push your computer to the max. 9 | If you want to save your computer from being at 99% cpu loadgo to the Tournament.cs file and change the variables WORKERS and BATCH_SIZE down from 20 to something smaller such as 5 or 3 10 | 11 | there are a couple of monopoly_population.txt files included: 12 | 13 | - 162 -> the brains I presented in my video that got to generation 162, they are quite good at monopoly (as far as I can tell) 14 | - champions -> an old set of brains from about generation 60 (bad players but not suicidal) 15 | - a blank text file -> this will start a new population at generation 1 16 | 17 | rename those to monopoly_population.txt if you would like to continue training them. 18 | 19 | --------------------------- -------------------------------------------------------------------------------- /monopoly_population.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b2developer/MonopolyNEAT/5706282faf6a13a4a522c2e71b469737222ab4b8/monopoly_population.txt --------------------------------------------------------------------------------