├── .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
--------------------------------------------------------------------------------