@RhysTaylor1/TalentVersusLuck
Python

Program to simulate how lucky events can lead to a power law of wealth distribution. Based on the method described in Pluchino et al. 2018.

fork
loading
Files
  • main.py
  • AgentFile.txt
  • AgentFileEnd.txt
  • EventFrequency.txt
  • EventPos.txt
  • LuckiestHistory.png
  • MoneyHistogram_PAPER.png
  • MoneyHistogram_SQUARE.png
  • MoneyLuckFucntion_PAPER.png
  • MoneyLuckFucntion_SQUARE.png
  • MoneySlope_PAPER.png
  • MoneySlope_SQUARE.png
  • MoneyTalentDistrubution_PAPER.png
  • MoneyTalentDistrubution_SQUARE.png
  • MoneyTime.png
  • MoneyUnLuckFucntion_PAPER.png
  • MoneyUnLuckFucntion_SQUARE.png
  • Pareto.png
  • RichMoneyPlots.png
  • RichMoneyTime.png
  • TalentHistogram_PAPER.png
  • TalentHistogram_SQUARE.png
  • TalentMoneyDistrubution_PAPER.png
  • TalentMoneyDistrubution_SQUARE.png
  • TalentTest.txt
  • UnluckiestHistory.png
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
# Talent versus luck simulation. This is based on a paper by Pluchino,
# Biondo & Rapisadra 2018 (https://arxiv.org/abs/1802.07068). My
# critique can be found here : 
# https://plus.google.com/u/0/+RhysTaylorRhysy/posts/WzQ3Awo8vC3

# Basically they're trying to model the wealth distribution in society
# and show that this can be enitrely due to luck. Their model has
# virtual "agents" (representing people) which have some "talent" and
# "wealth" parameters, and "events", which can increase or decrease 
# the agent's wealth. Agents are fixed but events move around. If an
# agent intersects an event, then if it's unlucky their wealth is
# halved. If it's lucky, they have a chance to double their wealth
# depending on their talent (so talentless people are never lucky, but
# even the most talented can be unlucky).

# The user parameter section is set by default to use the same values
# as in the paper. If you only want to play around with the model,
# that's the only bit you should need to alter. Otherwise keep reading.

import numpy
from numpy import pi
from numpy import arange
import random
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import ticker

# PART ONE - BASIC SETUP

# GLOBAL PARAMETERS - DEFAULT VALUES FROM ORIGINAL PAPER
# Starting money for each person
smoney = 10.0					# Default is 10.0

# Talent distribution parameters
# Distibution type can either be 'Gaussian' or 'Uniform'
tdist = 'Gaussian'		# Default is Gaussian

# If a Gaussian distribution is used
Tmean = 0.6						# Default 0.6
Tsigma = 0.1					# Default 0.1

# Number of people
nagents = 1000				# Default 1000

# Number of event objects
neventob = 500				# Default 500
# Should probably set the good/bad ratio, just use half for now (hardcoded)
# Try altering the number ! I bet it won't be the same as chaning the effect radius
# because adding more will add them at random positions, whereas chaning the radius
# will be more like adding more events at the same positions !

# Duration of simulation (i.e. years)
maxt = 40.0	          # Default 40, 10 is OK for testing

# Timestep
tstep = 0.5						# Default 0.5

# If the standard random walk method is used :
# Event movement speed
speed = 2.0						# Default 2.0
# Event distance threshold to occur:
edisthresh = 1.0			# Default 1.0

# Set which modules to use for evaluating the effect of the events
EventMethod = 'Standard'	# Default 'Standard'
# Default method is 'Standard', which uses agent objects moving on a
# random walk. Alternative is 'Fast', which has some fraction of people
# experiencing an event at each timestep.
# Standard method takes 8 minutes. Fast method takes 0.5 minutes, almost all
# of which is due to plotting.

# Set how the type of event (good or bad) is determined
EventTypeMethod = 'TalentOFF'	# Default is 'TalentOFF'
# If 'TalentON' is used, then talent influences whether an event will
# be lucky or unlucky
# If 'TalentOFF' is used, then the luck status is determined by the agents
# if using a random walk, and at random if using another method

# Set the event frequency distribution, in case the 'Fast' method is used.
# Defaults come from running 10 simulations and extracting the mean and
# sigma of the Guassian.
EventMeanF =  38.8125	# Default 38.8125
EventSigmaF = 6.32454 # Default 6.32454

# Font sizes for the plots
axeslabelsize = 10
axesnumbrsize = 8

# Method to evaluate the effect of events
# 'TalentON' means talent has a chance to allow a good event to succeed
# 'TalentOFF' means the event will convey benefits regardless of talent
GoodEventMethod = 'TalentON'	# Default TalentON
BadEventMethod  = 'TalentOFF' # Default TalentOFF

# Specify how talent can vary :
TalentTransform = 'None'
TalentShift = 0.1
# Ideas :
# - LearnFromBad : increase talent if a negative event happens
# - LearnFromGood: increase talent if a positive event happens
# - MoreExtreme : decrease if negative, increase if positive
# - MoreEven : increase if negative, decrease if positive
# - Increase : increase if any event happens
# - Decrease : decrease if any event happens
# - WithRandom : shift amount has a random variation
# - WithWisdom : create an additional wisdom parameter. Non-monatonic 
# variation : low wisdom people get worse, the average do nothing, the
# very wise increase.


# Output event position files for testing
Testfiles = True

# END OF GLOBAL PARAMETERS
# (Note that there is one more parameter that may need to be adjusted. If the
# total wealth becomes extreme, figure 3 (the money histogram) will fail to plot
# because the default bin size (10) is too small. Do a CTRL+F for "binwidth" and 
# Whack on a few extra zeros.


# NOTE : Fast method is working well, except that it's more of a 60:20 than 80:20
# rule. Compare distribution of number events by both methods - does random walk
# method really produce a Gaussian ?
# Measure mean number of events per agent by old method, both overall and only for
# agents who experience more than zero events.
#... or maybe we want number of agents who experience different numbers of events. 
# We could have a file : Time 0Events 1Event 2Events 3Events... where each column
# holds the number of agents that experience that many events. 
# To get a feel for how many columns we're going to need, do a "dry run" with the
# standard method that prints out the maximum number of events experienced by any
# agent at each timestep.
# The maximum number of events per agent seems to be similar for both methods,
# typically 1 or 2. Not much variation.
# Compare :
# - What fraction of timesteps have max. 1 event, what fraction have 2, for both
# methods
# Fast method : 1 event = 45; 2 events = 35, 3 events = 1
#                         40             40
#                         39             41
#                         37             43
# Slow method :           35             42             3
#                         30             49             1
#                         35             43             2             
#                         32             47             1
# OK, so there is a subtle but clear difference between the two methods.
# The fast method is less likely to give each agent multiple events than
# the slow method. The slow method is close to random, but not completely
# (earlier tests showed that if the event movement speed is altered
# slightly, the results are significantly different)

# But this is just comparing the maximum number of events of any agent.
# To get something useful, what we want is (for each timestep) :
# - No. agents who have no events
# - No. agents who have 1 event
# - No. agents who have 2 events
# - No. agents who have lucky/successful events
# Then we can redesign the fast method slightly.

# The easiest way to do this would be to add extra properites to agents :
# more counters, but these would be reset to zero every timestep, so they
# would only hold the counts for each timestep. Then the printout can just
# run through every agent at every timestep.

# After testing the above, the distributions are almost identical, with no
# sign of any overall difference. Bugger.

# Another possibility is that in the standard method agents are more likely 
# to encounter the same event objects repeatedly... let's begin by comparing the
# numbers of lucky events that occur by each method.
# Fast method : looks Gaussian. Mean = 18.6775, s.d. = 4.45348
# Slow method : looks Gaussian. Mean = 18.885,  s.d. = 4.28448
# Which seems to be basically identical.
# But this is the overall number of lucky events. We should consider the number
# per agent.
# Nope,  that's the same was well !

# Or maybe there's a bug in the code so that the random walk agents are better at
# exploiting lucky events ? Unlikely since modular...

# Another possibility is the boundary conditions. The events are reflected back
# if they stray outside, so maybe agents near the edges are disproportionately
# likely to encounter the same events twice. Compare what happens if we have
# the events move from the opposite side, as though the world was a torus.
# The Pluchino paper doesn't specify how they handle this, so it will be
# interesting in itself to compare the different methods.
# Nope that makes no difference either !

# But... the total wealth generated by the two methods is VERY different :
#Slow		  Fast
#20744.48	11244.06
#25214.78	11747.34
#20540.64	11437.34
#26259.72	11623.28
#31429.50	12152.54

# Maybe in the standard method, each agent has a disproportionately higher chance
# to encounter a lucky event twice. This might not show up in the number of events...

# Wealth of wealthiest individual ?
#Slow			Fast
#600			1200
#10,000		1200
#600			600
#5000			300
#1200			600

# Which are about the same. What we're looking for is consistency. The very wealthiest
# individuals are clearly experiencing the same typical event numbers.
# Total number of lucky events experienced by the 20% wealthiest people :
# Slow	Fast
# 525		412
# 532		421
# 470		377
# 502		393
# 517		405
# Mean 509, 401

# The problem with this is that even the luckiest agents aren't going to
# experience more than ~5 lucky events or so over their entire history. So
# the chance of any individual agent experiencing a lucky event per timestep
# is going to be very, very small. Even a slight change could have drastic
# consequences. Is there some metric we could use that would give less subtle
# effects, so we could employ it in a more reliable way ?

# For the sake of double-checking, compare the number of events that occur 
# per timstep by the two methods (total and only lucky). Does the fast method
# ensure that exactly half the events are lucky, does that matter ?
# What about a hybrid approach whereby instead of moving the events, their
# position is just set to be random ? Their distance from the agents would
# still have to be evaluated but it would simplify the movement calculation
# and increase speed.
# That does save a bit of time (1 min 20 c.f. 2 minutes) but not all that much
# but more interestingly, we get numbers in excellent agreement with the fast
# method (total wealth, no. lucky events of 20% richest, 60:20 ratio)
# Which confirms that the random-walk method is indeed not truly random. We
# just need to find some way to recreate this statistically...

# Perhaps the problem is that the per timestep distribution is just not a
# large enough sample to detect the distribution change. Let's measure :
# - The total no. lucky events for all agents, per simulation
# - The total no. unlucky events for all agents, per simulation
# Then we can divide by the number of agents and timesteps to get an accurate
# estimate of the true average.... ?
# (By comparing the two we'll be able to see if there's any tendency to favour
# lucky events, of if the greater wealth inequality simply results from more
# events in general)
# Heck let's start simple with the total number of events :
# Slow	Fast
# 2917	3046
# 2851	2945
# 2857	3103
# 2818	3129
# 2893	3114
# Mean : 2867, 3067

# Good events :
# Slow %			Fast %
# 1512 51.8		1522 50.0
# 1496 52.4		1467 49.8
# 1420 49.7		1572 50.7
# 1375 48.8		1570 50.2
# 1445 49.9		1535 49.3
# Mean : 1450, 1533
#        50.5, 50

# Bad events :
# Slow	Fast
# 1405	1524
# 1355	1478
# 1437	1531
# 1443	1559
# 1448	1579
# Mean : 1418, 1534

# Here's a question : does the wealth inequality originate more from the rich
# getting richer or the poor getting poorer ?

# Compare also number of unlucky events and events in general.
# Try and see if agents experience the same event object multiple times.

# Maybe if we make the chance of an event being lucky to be sliiightly higher... nope
# that doesn't have much effect at all unless we go to extremes.

# Here's what we know :
# - The maximum number of events experienced by any agent at any timestep tends to be 
# higher with the random walk method 
# - The number of lucky events experienced by the wealthiest agents is higher for the
# random walk method
# - The overall number of events is lower with the random walk method
# - The event per agent per timestep distribution is identical for both methods
# - Both methods give an almost exactly equal fraction of good and bad events
# - The total wealth generated by the random walk method is much higher
# - The wealth of the wealthiest individual is comparable by both methods (maybe a bit
# higher sometimes with the random walk)
# - If we completely randomise the event positions each timstept, instead of doing a walk, 
# we get an excellent agreement (total wealth, wealth fraction of richest 20%) with the fast
# method
# PART TWO - INITIALISE WORLD AND SET MODULES TO EVALUATE EVENTS

print('Initialising world...')
# Counters of the number of events agents actually experience, 
# regardless of the number present
# in the world.
nevents = 0     
ngood = 0       
nbad = 0        
nsuccess = 0    
# Good events aren't always successful - this depends on talent to 
# exploit them. In this simulation, bad events are always bad.
# ALSO SET A SUCCESS COUNTER FOR BAD EVENTS !

# Create a Pareto array that will holds the wealth fraction of the 20% 
# richest and most talented people as a function of time 
# [0 = time, 1= richest, 2 = talented]
pareto = numpy.zeros((int(maxt/tstep),3))

# Create people as objects. 
# NLucky and NUnlucky are just counters of how many events they experience
# in total.
# NEvents is a counter of the number of events they experience at any given
# timstep.
# X and y are their positions on a grid for the random walk method.
# tl is an array which holds the wealth and number of events as a
# function of time.
class Person:
    def __init__(self,name,Talent,Money,NLucky,NUnlucky,NEvents,x,y,tl):
        self.Name = name
        self.Talent = Talent
        self.Money = Money
        self.NLucky = NLucky
        self.NUnlucky = NUnlucky
        self.NEvents = NEvents				
        self.x = x
        self.y = y
        self.tl = tl

# Create events as objects. These are just pointers that move around 
# (the people are fixed).
# Whent people and events intersect, the agent experiences a lucky or 
# unlucky event, if the random walk method is used. Other methods are 
# purely probabilistic and do not use events as objects. I found that
# the other methods (scroll to the bottom) give very different results
# to the original paper so those are commented out and only the random 
# walk method is used.
# The paper has these events as being of fixed good or bad luck.
# Currently experimenting with a new method that should give the same
# results as the random walk but faster.
class Event:
    def __init__(self,x,y,luck):
        self.x = x
        self.y = y
        self.luck = luck

# Empty list we will fill with people. We will identify each person as
# items in the list. 
agent = list() 

# Empty list we will fill with events
event = list()
       
# Create agents (people) with different talents 
for i in range(nagents):
    # Set the variables of the person we're about to create
    AgentName = 'Agent'+'_'+str(i)
    # Set distribution of talent
    if tdist == 'Gaussian':
        AgentTalent = numpy.random.normal(Tmean,Tsigma)
    if tdist == 'Uniform':
        AgentTalent = random.random()
    # Truncate the talent range (needed for Gaussian)
    if AgentTalent < 0.0:
        AgentTalent = 0.0
    if AgentTalent > 1.0:
        AgentTalent = 1.0
		# Everyone gets the same starting wealth
    AgentMoney = smoney
		# Counters of the events they experience (doesn't include successful
		# good/bad events yet)
    AgentNLucky = 0
    AgentNUnlucky = 0
		# Counter of events per timestep
    NEvents = 0
		# Set their position
    AgentX = random.random()*201.0
    AgentY = random.random()*201.0
		# Give them a list holding the current time, no. lucky events, and 
		# money
    AgentTL = []
    
    # Add a new person with the specified properties to the list
    agent.append(Person(AgentName,AgentTalent,AgentMoney,AgentNLucky,AgentNUnlucky,NEvents,AgentX,AgentY,AgentTL))

# Each person will now be an object at a fixed position in the agent 
# list, e.g. this will tell us agent 400's talent :    
#print(agent[400].Talent)

# Now create the event objects with random positions and make half of
# them lucky...
for i in range(int(float(neventob)/2.0)):
    EventX = random.random()*201.0
    EventY = random.random()*201.0
    EventLuck = 'Good'
    event.append(Event(EventX,EventY,EventLuck))

# ... and the other half unlucky    
for i in range(int(float(neventob)/2.0),neventob):
    EventX = random.random()*201.0
    EventY = random.random()*201.0
    EventLuck = 'Bad'
    event.append(Event(EventX,EventY,EventLuck))
    
# We now have events as objects, e.g. :
#print(event[0].x)

# Set the luck status if events occur at random or are influenced by
# talent. If they're fixed by the event's status as in the standard
# method then this is done in the main wrapper. 
def LuckStatus(atalent):
	# Case 1 : Talent doesn't do anything. Luck status is random
   if EventTypeMethod == 'TalentOFF':	
       enumber = random.random()
       if enumber > 0.5:
           eluck = 'Good'
       if enumber <= 0.5:
           eluck = 'Bad'

   # Case 2 : Talent influences whether events will be lucky or unlucky
	 # (but not if they will occur in the first place). Only used for fast
	 # method.
   if EventTypeMethod == 'TalentON':
       Tnumber = random.random()
       if Tnumber < atalent:
           eluck = 'Good'
       if Tnumber >= atalent:
           eluck = 'Bad'

   return eluck

		
# Have an agent experience an event
# an = agent number
# etpye = talent on or off 
# estatus = good or bad luck
def shithappens(an,etype,elstatus):
   global nsuccess
   # Regardless of the event type, increase the counter. Recall that this
	 # gets reset every timestep in the main module.
   agent[an].NEvents = agent[an].NEvents + 1

	 # Modification to the usual counter for testing purposes : have it only
	 # count lucky events.
   if elstatus == 'Good':
       agent[an].NEvents = agent[an].NEvents + 1

	 # Case 1 : chance of event happening is determined by talent 
   if etype == 'TalentON':
       Tnumber = random.random()
       if elstatus == 'Good':  
           if Tnumber < agent[an].Talent:
               nsuccess = nsuccess + 1						 
               agent[i].Money = agent[i].Money * 2.0
               agent[i].NLucky = agent[i].NLucky + 1
               agent[i].tl.append([time,agent[i].NLucky,agent[i].Money])
       if elstatus == 'Bad':
           if Tnumber > agent[i].Talent:
               agent[i].Money = agent[i].Money / 2.0
               agent[i].NUnlucky = agent[i].NUnlucky + 1
               agent[i].tl.append([time,agent[i].NLucky,agent[i].Money])
   
	 # Case 2 : events are not influenced by talent at all
   if etype == 'TalentOFF':
       if elstatus == 'Good':  
           agent[i].Money = agent[i].Money * 2.0
           agent[i].NLucky = agent[i].NLucky + 1
           agent[i].tl.append([time,agent[i].NLucky,agent[i].Money])
       if elstatus == 'Bad':
           agent[i].Money = agent[i].Money / 2.0
           agent[i].NUnlucky = agent[i].NUnlucky + 1
           agent[i].tl.append([time,agent[i].NLucky,agent[i].Money])							 

# Here alter the agent's talent according to the methods : 
# - LearnFromBad : increase talent if a negative event happens
# - LearnFromGood: increase talent if a positive event happens
# - MoreExtreme : decrease if negative, increase if positive
# - MoreEven : increase if negative, decrease if positive
# - Increase : increase if any event happens
# - Decrease : decrease if any event happens
# - WithRandom : shift amount has a random variation
# - WithWisdom : create an additional wisdom parameter. Non-monatonic 
# variation : low wisdom people get worse, the average do nothing, the
# very wise increase.
# With the amount to adust the talent being the parameter called 
# "TalentShift"



# Count the number of agents who experience different numbers of events.
# Useful for comparing the distributions produced by different methods.
# Only for testing purposes really.
def eventcounter():
   nzero = 0
   nOne = 0
   ntwo = 0
   nthree = 0

   for i in range(nagents):
       if agent[i].NEvents == 0:
	         nzero = nzero + 1
       if agent[i].NEvents == 1:
	         nOne = nOne + 1	
       if agent[i].NEvents == 2:
	         ntwo = ntwo + 1
       if agent[i].NEvents == 3:
	         nthree = nthree + 1

   print(time,nzero,nOne,ntwo,nthree)



# PART THREE -  THE SIMULATION

# Evaluate the people's wealth over the 40 year course of their working
# life.
# Every six months they have a chance of a lucky or unlucky event which
# can alter their wealth.
# Lucky events have a chance of success proportional to the agent's 
# talent. Unlucky events are always successful.
# Three different methods are given for determining whether an event 
# occurs at each timestep, though only the random walk one described in
# the paper is used.

# List holding the number of events occurring over time, used for figuring
# out how often they occur on average. 
neventstime = numpy.zeros((int(maxt/tstep)))

# List holding the number of events that occur to individual agents
nagenteventlist = []

print('Beginning simulation...')

# OPTIONAL FOR TESTING
if Testfiles == True:
	# 1 - Create a file of the agent positions.
	agentfile = open('AgentFile.txt','w')
	agentfile.write(str(nagents)+'\n')
	agentfile.write('1'+'\n')	# Only need the first timestep, agents don't move
	for i in range(nagents):
		xp = str(agent[i].x)
		yp = str(agent[i].y)
		agentfile.write(xp+' '+yp+'\n')
	agentfile.close()

	# 2 - Create a file of the event positions, which will hold the values for
	# each timestep. This only works for the standard method ! 
	if EventMethod == 'Standard':
		# First open in write mode (will erase existing data) and set the header
		efile = open('EventPos.txt','w')
		efile.write(str(neventob)+'\n')
		efile.write(str(int(maxt/tstep))+'\n')
		efile.close()

		# Now open the file again in append mode; future calls to the file will
		# add the data
		efile = open('EventPos.txt','a')


# Notes : reflection method gives sensible event positions but the toroidal
# method has some leakage.
# We also need to output the richlist so that we can look at the richest agents
# to see if they have more repeat encounters than expected.
# Think about a statistic way to measure the number of repeat encounters.
# Did I already check the number of lucky events experienced by the richlist by
# both methods ?

# HERE THE SIMULATION BEGINS
for time in numpy.arange(0.0,maxt,tstep):
   # OPTIONAL - once the event has been moved, record its position in the event file
   for j in range(neventob):
       xp = str(event[j].x)
       yp = str(event[j].y)
       efile.write(xp+' '+yp+'\n')	 

	 # Optionally reset the list every timstep
   #nagenteventlist = [] 
   ncurrlucky = 0

  # Reset the event counter for each agent to zero
   for i in range(nagents):
       agent[i].NEvents = 0

   if EventMethod == 'Fast':
       # New method that should be faster. Based on 10 runs, we have the
	     # frequency distribution of events. Draw a number from this 
	     # Gaussian and then select that many agents at random to be 
		   # experiencing an event. We know from other tests that it doesn't
		   # matter if the luck status is assigned purely at random, rather
		   # than from some fixed value (so long as we had the agents moving
		   # at the same rate as in the paper).
       donevents = int(numpy.random.normal(EventMeanF,EventSigmaF))

       # With the Fast method, we know the total number of events that will
			 # happen already, so we can just set this directly.
       neventstime[int(time/tstep)] = donevents
       
			 # Create a list of random agents. May have some agents twice. Total
			 # length will be equal to the number of events parameter.
       agentlist = []
       for i in range(donevents):
           agentnumber = int(random.random()*float(nagents))
           agentlist.append(agentnumber)

       for i in agentlist:
           nevents = nevents + 1
           # Set the luck status of the event that will occur. At present there's
					 # no module to define the luckstatus based on talent.
           if EventTypeMethod == 'TalentOFF':
               luckstatus = LuckStatus(0.0)
           if EventTypeMethod == 'TalentON':
               luckstatus = LuckStatus(agent[i].Talent)
           if luckstatus == 'Good':
               shithappens(i,GoodEventMethod,luckstatus)
               ngood = ngood + 1
               ncurrlucky = ncurrlucky + 1
           if luckstatus == 'Bad':
               shithappens(i,BadEventMethod,luckstatus)
               nbad = nbad + 1

					 # The numpy.unique function can tell us the maximum number of times an
					 # agent appears more than once, i.e. the highest number of events 
					 # experienced by individual agents.
           #print(numpy.max(numpy.unique(agentlist,return_counts=True)[1]))
           nagenteventlist.append(numpy.max(numpy.unique(agentlist,return_counts=True)[1]))

       #print(time,max(nagenteventlist))

   if EventMethod == 'Standard':
		# Counter for getting the event frequency distribution. Not needed.
    ncurrentevents = 0

    nagenteventlist = []
    for i in range(nagents):
        # Classical random walk method as described in the paper. First
				# check if the agents are experiencing any events, and if so, 
				# update their money accordingly. 
        nagentevents = 0.0
        agent[i].tl.append([time,agent[i].NLucky,agent[i].Money])
        ax = agent[i].x
        ay = agent[i].y
        for j in range(neventob):
            ex = event[j].x
            ey = event[j].y
            
            # By default, assume the distance is too high. But if the linear
						# distances are both less than the treshold, then evaluate it more
						# precisely. This saves a great deal of time.
            distance = 1000.0
            if (abs(ex-ax) <= edisthresh) and (abs(ey-ay) <= edisthresh):						
               distance = numpy.sqrt((ax-ex)**2.0 + (ay-ey)**2.0)
            
            # An event happens if the agent and event are close enough
            if distance <= edisthresh:							
               nevents = nevents + 1
               ncurrentevents = ncurrentevents + 1
               nagentevents = nagentevents + 1

               # Assign luck status of the event. Only use the module if talent
							 # is set to affect luck. If it doesn't, just use the event's
							 # intrinsic luck value.
               if EventTypeMethod == 'TalentON':
                   luckstatus = LuckStatus(agent[i].Talent)
               if EventTypeMethod == 'TalentOFF':
                   luckstatus = event[j].luck

               # New modular code
               #if event[j].luck == 'Good':
               if luckstatus == 'Good':
                   ngood = ngood + 1
                   ncurrlucky = ncurrlucky + 1									 
                   shithappens(i,GoodEventMethod,'Good')									 
               #if event[j].luck == 'Bad':
               if luckstatus == 'Bad':
                   nbad = nbad + 1								 
                   shithappens(i,BadEventMethod,'Bad')

        
        nagenteventlist.append(nagentevents)        

   # Either just print the time :
   print(time)
	 # Or, optionally, print the time and event distributions
   #eventcounter()
   # Or, optionally, print the time and number of lucky events that ocurred in this
	 # timsetep
   #print(time,ncurrlucky)


   #print(time,numpy.max(nagenteventlist))

   # With the standard method we have to count the events after they happen
	 # to record the total.
   #neventstime[int(time/tstep)] = ncurrentevents
   #print('Number of events that occurred :',ncurrentevents)

   # Having evalutated the events happening to each agent, move every 
	 # event some units in a random direction.
   for j in range(neventob):
		  # Simplest method - just reset positions to random.		
      # event[j].x = random.random()*201.0
      # event[j].y = random.random()*201.0	

			 # Slower method - do a random walk		                    
       ex = event[j].x
       ey = event[j].y
       theta =numpy.radians(random.random()*360.0)
       shiftx = speed*numpy.sin(theta)
       shifty = speed*numpy.cos(theta)

			 # Normal conditions for random walk
       if (ex+shiftx >= 0.0) and (ex+shiftx <= 201.0):
           event[j].x = event[j].x + shiftx
       if (ex+shifty >= 0.0) and (ex+shifty <= 201.0):
           event[j].y = event[j].y + shifty

       # Keep the events within the boundaries
			 # Method 1 : reflection
       if (ex+shiftx > 201.0) or (ex+shiftx < 0.0):
           event[j].x = event[j].x - shiftx				 		           
       if (ey+shifty > 201.0) or (ey+shifty < 0.0):
           event[j].y = event[j].y - shifty

       # Method 2 : toroidal space. Move events to equvialent positions on
			 # the opposite sides of the world.
			 # This is slightly bugged, there's some leakage at the corners
       #if (ex+shiftx > 201.0):
       #    event[j].x = (ex+shiftx) - 201.0
       #if (ex+shiftx < 0.0):
       #    event[j].x = 201.0 + (ex+shiftx) # Since e+s must be -ve here
       #if (ey+shifty > 201.0):
       #    event[j].y = (ex+shifty) - 201.0
       #if (ey+shifty < 0.0):
       #    event[j].y = 201.0 + (ex+shifty) # Since e+s must be -ve here


				# Alter the luck status
        #if random.random() <=0.5:
        #    if event[j].luck == 'Good':
        #        event[j].luck == 'Bad'
        #    if event[j].luck == 'Bad':
        #        event[j].luck == 'Good'
        #if j > (int(float(neventob)/2.0)):
        #    if event[j].luck == 'Good':
        #        event[j].luck == 'Bad'
        #    if event[j].luck == 'Bad':
        #        event[j].luck == 'Good'

        #efile.write(str(event[j].x)+' '+str(event[j].y)+'\n')

		# Let's try moving the agents as well !
    #for j in range(nagents):                   
    #    ex = agent[j].x
    #    ey = agent[j].y
    #    theta =numpy.radians(random.random()*360.0)
    #    shiftx = 2.0*numpy.sin(theta)
    #    shifty = 2.0*numpy.cos(theta)
        
        # Keep the events within the boundaries
    #    if (ex+shiftx > 201.0) or (ex+shiftx < 0.0):
    #        agent[j].x = agent[j].x - shiftx
    #    if (ex+shiftx >= 0.0) and (ex+shiftx <= 201.0):
    #        agent[j].x = agent[j].x + shiftx
    #        
    #    if (ey+shifty > 201.0) or (ey+shifty < 0.0):
    #        agent[j].y = agent[j].y - shifty
    #    if (ex+shifty >= 0.0) and (ex+shifty <= 201.0):
    #        agent[j].y = agent[j].y + shifty
        
    #    #efile.write(str(event[j].x)+' '+str(event[j].y)+'\n')
	

	 # Find the total wealth fraction owned by the 20% richest and 20% most
	 # talented
   # Create an array that will hold money, talent of all agents
   mtarray= numpy.zeros((nagents,2))
   for i in range(nagents):
       mtarray[i,0] = agent[i].Money
       mtarray[i,1] = agent[i].Talent

   TotalMoney = numpy.sum(mtarray[:,0])
		
	 # Number of agents corresponding to 20% of the total
   fifthfrac = int(float(nagents)/5.0)

   # First get the wealth fraction owned by the 20% richest people. Sort
	 # array by wealth so we can then sum over the final 20%.
   mtarray = mtarray[mtarray[:,0].argsort()]
   TopFifthWealth = numpy.sum(mtarray[nagents-fifthfrac:nagents,0])

	 # Now we can find the wealth fraction of the 20% most talented people.
	 # Sort the array by the talent column and again sum the top fifth.
   mtarray = mtarray[mtarray[:,1].argsort()]
   MostTalentedWealth = numpy.sum(mtarray[nagents-fifthfrac:nagents,0])

   TopFrac = (TopFifthWealth/TotalMoney)*100.0
   WealthFrac = (MostTalentedWealth/TotalMoney)*100.0
   pareto[int(time/tstep),0] = time
   pareto[int(time/tstep),1] = TopFrac
   pareto[int(time/tstep),2] = WealthFrac

  

efile.close()	
# MUST USE BRACKETS ! Program will run if omit them, but the file will not
# be correct !!!


print('Filling in missing data...')
# We now need to 'fill in the blanks' if we've used the fast method, since
# this doesn't update the history array for every agent
if EventMethod == 'Fast':
   for i in range(nagents):
			 # Initialise blankhistory array. Can be the same for everyone, but we'll
			 # need to change this if we change the initial wealth distribution
			 # Double [] means we create a list of lists. This is useful because we're
			 # going to be appending lists to it later.
       blankhistory = [[0.0,0.0,smoney]]


      # Go through the existing agent's data. This ONLY holds values when
			# an event happened
       for j in range(len(agent[i].tl)):
           current_t = agent[i].tl[j][0]

           # Initial values for the blankhistory array index and time value
           bhi = len(blankhistory)-1
           bht = blankhistory[bhi][0]
           #print('Agent ',i,' length =',len(blankhistory), 'time =',bht)

           # If the time in the agent's array is the same as the last value
					 # in the new array, which will have the previous values for the
					 # money and luck parameters, we just copy the agents current
					 # values straight on.
					 # This is only needed if an event happens at t=0 or multiple events
					 # happen in a single timestep.
           if current_t == bht:
               blankhistory.append(agent[i].tl[j])

           # If the current time is higher than the last value in the new
					 # array, then we need to fill in the blanks with copies of the
					 # last values in the new array.
					 # We need to advance the time BEFORE adding a copy otherwise we'll
					 # get exact duplicate entries whenever an event occurs.
           if current_t >bht:
               thist = blankhistory[bhi][0]
               for k in range(int((current_t-bht)/tstep)):
                   thist = thist + tstep
                   blankhistory.append([thist,blankhistory[bhi][1],blankhistory[bhi][2]])
                   
							 # Now that the blanks have been filled we can add the missing value.
							 # This must be done at the point, as on the next loop will advance
							 # the current time again so it will just fill in more blanks.
               blankhistory.append(agent[i].tl[j])

       # Most likely the last value in the event list won't occur at the final
			 # timestep so fill this in to the end.
       bhi = len(blankhistory) - 1
       bht = blankhistory[bhi][0]
       thist = bht

       if thist < maxt:
           for k in range(int((maxt-thist)/0.5)):
		            thist = thist + 0.5
		            blankhistory.append([thist,blankhistory[bhi][1],blankhistory[bhi][2]])

       # Now we can replace the agent's array with the corrected one								
       agent[i].tl = blankhistory                   


# Create a file of the final agent positions and values.
agentfile = open('AgentFileEnd.txt','w')
# If want to do animations, use this header :
#agentfile.write(str(nagents)+'\n')
#agentfile.write('1'+'\n')	# Only need the first timestep, agents don't move
# Otherwise just create a header
agentfile.write('#X Y Money Talent NEvents'+'\n')
for i in range(nagents):
	xp = str(agent[i].x)
	yp = str(agent[i].y)
	m =  str(agent[i].Money)
	t =  str(agent[i].Talent)
	ne = str(agent[i].NLucky + agent[i].NUnlucky)
	agentfile.write(xp+' '+yp+' '+m+' '+t+' '+ne+'\n')
agentfile.close()


# PART FOUR - OUTPUT RESULTS AND PLOTTING
        
# Print out some basic statistics and write the output to a text file            
print('Number of events that occurred :',nevents)  
print('Number of good events :',ngood)
print('Number of successful explotiations of good events :',nsuccess)
print('Number of bad events :',nbad)
print('Maximum no. events for any agent :',max(nagenteventlist))

# Check if the 80:20 rule is true - the wealthiest 20% should own 80%
# of the money. 
# Since the Pareto fractions have already been computed we don't need to
# extract them again.
print('The total amount of wealth is :','%.2f' % TotalMoney) 
print('The 20% richest people contain','%.2f' % TopFrac,'% of the wealth')
print('The 20% most talented people have','%.2f' % WealthFrac, '% of the wealth')

# Below, the number of lucky events for the richest people is computed with the
# Pareto plot.


# Save the main results to a file for plotting            
outfile = open('TalentTest.txt','w')
outfile.write('#Talent Money NLucky NUnlucky'+'\n')

for i in range(1000):
    outfile.write(str(agent[i].Talent)+' '+str(agent[i].Money)+' '+str(agent[i].NLucky)+' '+str(agent[i].NUnlucky)+'\n')
    
outfile.close()

# Save the event frequency distribution to another file
eventfile = open('EventFrequency.txt','w')
eventfile.write('#Time NEvets'+'\n')
for i in range(int(maxt/tstep)):
	eventfile.write(str(i)+' '+str(neventstime[i])+'\n')

eventfile.close()            


# PLOTTING

# LaTeX-like fonts in the figure
plt.rc('font', family='serif')

matplotlib.rcParams['xtick.labelsize'] = 14 
matplotlib.rcParams['ytick.labelsize'] = 14

from matplotlib.ticker import AutoMinorLocator
XminorLocator = AutoMinorLocator()
YminorLocator = AutoMinorLocator()

datafile = open('TalentTest.txt','r')

data= numpy.genfromtxt(datafile, skip_header=1)
talent = data[:,0]
money = data[:,1]

mmin = numpy.min(money)
mmax = numpy.max(money)

tmin = numpy.min(talent)
tmax = numpy.max(talent)


# Figure 1 - Scatter plot of talent versus wealth. This is figure 4a in
# the paper.
# For reference this is how I figured out how to do logarithmic axes with
# correct ticks :
#https://stackoverflow.com/questions/24866419/aspect-ratio-in-semi-log-plot-with-matplotlib
#https://matplotlib.org/api/_as_gen/matplotlib.pyplot.xticks.html
#https://repl.it/@RhysTaylor1/Plot-test-program
LogMoney = numpy.log10(money)

print('Plotting figure 1')
plt.figure(1)
ax = plt.gca()
ax.scatter(LogMoney,talent)
lowx = numpy.log10(mmin/2.0)
uppx = numpy.log10(mmax*2.0)

# We have to manually specify the axes labesl BEFORE setting the aspect
# ratio ! If we do it the other way around, matplotlib adds annoying
# padding.
locs, labels = matplotlib.pyplot.xticks()

newlabels = []
newlocs = []
for value in locs:
	if int(value) == value:
		newlabels.append(10.0**value)
		newlocs.append(value)

matplotlib.pyplot.xticks(newlocs, newlabels)

# We know the y axis can only ever range between 0 and 10 by definition
ax.set_ylim([0.0,1.0])
ax.set_xlim([lowx,uppx])

minor_ticks = []
for i in range(int(min(locs)),int(max(locs))):
    for j in range(2,10):
         minor_ticks.append(i+numpy.log10(j))

plt.gca().set_xticks(minor_ticks, minor=True)

# Square plot - just set aspect ratio to xrange since yrange = 1.0
plt.gca().set_aspect((uppx-lowx),adjustable='box')

# Label axes
# Set font - size will be used for all axes titles in all plots
csfont = {'fontname':'Serif','fontsize':axeslabelsize}
plt.xlabel('Money', **csfont)
plt.ylabel('Talent', **csfont)

# The size of the numerical labels of the ticks on ONLY this plot
plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('TalentMoneyDistrubution_SQUARE.png',dpi=100,facecolor='white',bbox_inches='tight')

# Second version : a spect ratio set manually to match figure in paper
plt.gca().set_aspect((uppx-lowx)/2.5,adjustable='box')

plt.savefig('TalentMoneyDistrubution_PAPER.png',dpi=100,facecolor='white',bbox_inches='tight')


# Figure 2 - Scatter plot of Wealth verus talent with vertical lines.
# This is figure 4b in the paper.
print('Plotting figure 2')
plt.figure(2)
ax = plt.gca()
ax.scatter(talent, money)

# Force the x-axis to be in the talent range. Let the y-axis vary, but get
# its values so we can set the aspect ratio
ax.set_xlim([tmin,tmax])
miny = ax.get_ylim()[0]
maxy = ax.get_ylim()[1]

# Plot vertical lines from each point
for i in range(len(data)):
  plt.plot((talent[i],talent[i]),(0.0,money[i]),'b-')

plt.ylabel('Money', **csfont)
plt.xlabel('Talent', **csfont)

plt.gca().set_aspect((tmax-tmin)/(maxy-miny),adjustable='box')

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('MoneyTalentDistrubution_SQUARE.png',dpi=100,facecolor='white',bbox_inches='tight')

# Second version with aspect ratio same as paper
plt.gca().set_aspect((tmax-tmin)/(2.5*(maxy-miny)),adjustable='box')

plt.savefig('MoneyTalentDistrubution_PAPER.png',dpi=100,facecolor='white',bbox_inches='tight')



# Figure 3 - Histogram of wealth, figure 3a in the paper.
print('Plotting figure 3')
plt.figure(3)
binwidth = 10.0

# We want a logarithmic y-axis but a linear x-axis. Matplotlib won't let us
# set the aspect ratio in semi-log plots. So what we'll do is first bin the
# data without actually plotting it and extract the parameters of the bins.
# Then, below, we'll make a bar plot using the extracted data.
# "n" is the number count in each bin
# "bins" is the left hand edge of each bin 
# "Patches" is something to do with rectangles
(n,bins,patches) = plt.hist(money, bins=arange(-binwidth/2.0, max(money) + binwidth, binwidth), color='red', histtype='stepfilled')

# Logarithmic histogram, like in the paper
# Take log of bin heights. Now for most values, where the count is > 1,
# this is fine. We can just plot the bar height as normal. And the count
# can't be less than 1 since it's a discrete variable.
# But the count can and is often equal to 1. So to display bars, we must
# have them starting at a value that's slightly negative in log space
# (remembering that the labels will be corrected).
# To ignore cases where the count was zero, the heights are set to nan.
# Cases where the count was 1 have heights set to zero, but since the
# bars start at -1.0, they'll appear to reach +1.0 (given the label
# corrections).
# Note that we get the original counts from n, which is obtained in the
# previous plot.

# Just in case the plot still exists
plt.clf()

plt.figure(3)

# This is now working, but we still need to label the axes and remove
# the above plot while preseving the values we need... also do the second
# version with the paper's aspect ratio, change the figure number.

# Correct the heights to logarithmic space
fixheights = numpy.zeros((len(n)+1))
# We need extra bars to fill in the negative bits
extraheights = numpy.zeros((len(n)+1))
for i in range(len(n)):
	if n[i] > 0.0:
		fixheights[i] = numpy.log10(n[i])
		extraheights[i] = -1.0
	# We want to avoid plotting anything if the true count was zero
	if n[i] == 0.0:
		fixheights[i] = numpy.nan
		extraheights[i] = numpy.nan


plt.bar(bins,fixheights,binwidth,color='red')
# Add bars extending to fill the negative regions
plt.bar(bins,extraheights,binwidth,color='red')

ax = plt.gca()

plt.ylabel('Count', **csfont)
plt.xlabel('Money', **csfont)

minx = ax.get_xlim()[0]
maxx = ax.get_xlim()[1]
miny = ax.get_ylim()[0]
maxy = ax.get_ylim()[1]

#ax.set_yscale('linear')

ax.set_ylim([-1.0,maxy])

# Do the tricks to get logarithmic values on the y-axis
locs, labels = matplotlib.pyplot.yticks()

newlabels = []
newlocs = []
for value in locs:
	if int(value) == value:
		newlabels.append(10.0**value)
		newlocs.append(value)

matplotlib.pyplot.yticks(newlocs, newlabels)

minor_ticks = []
for i in range(int(min(locs)),int(max(locs))):
    for j in range(2,10):
         minor_ticks.append(i+numpy.log10(j))

plt.gca().set_yticks(minor_ticks, minor=True)

plt.gca().set_aspect((maxx-minx)/((maxy+1.0)),adjustable='box')

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('MoneyHistogram_SQUARE.png',dpi=100,facecolor='white',bbox_inches='tight')

# Second version : a spect ratio set manually to match figure in paper
plt.gca().set_aspect((maxx-minx)/(2.5*(maxy+1.0)),adjustable='box')

plt.savefig('MoneyHistogram_PAPER.png',dpi=100,facecolor='white',bbox_inches='tight')



# Figure 4 - Slope of wealth distribution, figure 3b in the paper. Uses
# variables set in figure 3.
print('Plotting figure 4')
plt.figure(4)

plt.xlabel('Money', **csfont)
plt.ylabel('Number',**csfont)

nmin = min(n[n>0.0])
nmax = max(n)

ax = plt.gca()
ax.scatter(bins[0:len(bins)-1]+(binwidth/2.0), n)
ax.set_xscale('log')
ax.set_yscale('log')
# Want labels in standard notation, not logarithmic... has problems below 0
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter("%d"))
ax.yaxis.set_major_formatter(ticker.FormatStrFormatter("%d"))
ax.set_xlim([binwidth/2.0,mmax])  # Increase range for aesthetics
ax.set_ylim([nmin,nmax*2.0])

# Sincw we're working in log-log space, have to convert values to logs
# for calculating apsect ratio
xllim = numpy.log10(binwidth/2.0)
xulim = numpy.log10(mmax)
yllim = numpy.log10(nmin)
yulim = numpy.log10(nmax*2.0)


# We now need to add a power law of slope -1.33 as per the original 
# paper
# N = k*money^-1.33 where money is given by the bin vale
klist = []
for i in range(len(n)):
	if (bins[i] > 0.0) and (n[i] > 0):
		ktemp = bins[i]**(-1.33) / n[i]
		klist.append(ktemp)

klist = numpy.asarray(klist)
try:
	k = numpy.nanmean(klist)
except:
	k = numpy.mean(klist[klist>0.0])
k = 1.0/k

# We now need a list of x and y points to plot, let's just use two :
m1 =[bins[1]+binwidth/2.0,bins[len(bins)-1]+binwidth/2.0]
y1 = [k*m1[0]**(-1.33),k*m1[1]**(-1.33)]

ax.plot(m1,y1,color='black')

plt.gca().set_aspect((xulim-xllim)/(yulim-yllim),adjustable='box')

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('MoneySlope_SQUARE.png',dpi=100,facecolor='white',bbox_inches='tight')

plt.gca().set_aspect(((xulim-xllim)/(2.31*(yulim-yllim))),adjustable='box')

plt.savefig('MoneySlope_PAPER.png',dpi=100,facecolor='white',bbox_inches='tight')



# Figure 5 - No. lucky events as a function of wealth, figure 5a in
# paper. Note that it isn't clear if "lucky" means "successful" here. I have
# used successful lucky events.
print('Plotting figure 5')
wealthlist = numpy.zeros((nagents))
lucklist   = numpy.zeros((nagents))
unlucklist = numpy.zeros((nagents))	# This will be used in the next figure

for i in range(nagents):
	wealthlist[i] = agent[i].Money
	lucklist[i]   = agent[i].NLucky
	unlucklist[i] = agent[i].NUnlucky

maxwealth = numpy.max(wealthlist)
maxluck = numpy.max(lucklist)
minluck = numpy.min(lucklist)
minunluck = numpy.min(unlucklist)
maxunluck = numpy.max(unlucklist)

plt.figure(5)
logwealthlist = numpy.log10(wealthlist)
ax = plt.gca()
ax.scatter(logwealthlist,lucklist)

lwlim = numpy.log10(min(wealthlist)/2.0)
uwlim = numpy.log10(max(wealthlist*2.0))

locs, labels = matplotlib.pyplot.xticks()

# Fix the x-axis ticks and locations since they need to be logarithmic
newlocs = []
newlabels = []
for value in locs:
	if int(value) == value:
		newlabels.append(10.0**value)
		newlocs.append(value)

matplotlib.pyplot.xticks(newlocs, newlabels)

ax.set_ylim([minluck,maxluck])
ax.set_xlim([lwlim,uwlim])

minor_ticks = []
for i in range(int(min(locs)),int(max(locs))):
    for j in range(2,10):
         minor_ticks.append(i+numpy.log10(j))


plt.gca().set_xticks(minor_ticks, minor=True)

plt.gca().set_aspect((uwlim-lwlim)/(maxluck-minluck),adjustable='box')

plt.xlabel('Money', **csfont)
plt.ylabel('No. lucky events', **csfont)

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('MoneyLuckFucntion_SQUARE.png',dpi=100,facecolor='white',bbox_inches='tight')

# Same but with aspect ratio of paper
plt.gca().set_aspect((uwlim-lwlim)/(2.5*(maxluck-minluck)),adjustable='box')
plt.savefig('MoneyLuckFucntion_PAPER.png',dpi=100,facecolor='white',bbox_inches='tight')

plt.clf()



# Figure 6 - No. unlucky events as a function of weatlh, figure 5b in
# paper. We already have everything we need already, so we just need to plot a
# new figure
print('Plotting figure 6')
plt.figure(6)
ax = plt.gca()
ax.scatter(logwealthlist,unlucklist)

#locs, labels = matplotlib.pyplot.xticks()

# Already have these from fig. 5 and the x-axis isn't going to vary
matplotlib.pyplot.xticks(newlocs, newlabels)

ax.set_ylim([minunluck,maxunluck])
ax.set_xlim([lwlim,uwlim])

minor_ticks = []
for i in range(int(min(locs)),int(max(locs))):
    for j in range(2,10):
         minor_ticks.append(i+numpy.log10(j))

plt.gca().set_xticks(minor_ticks, minor=True)

plt.gca().set_aspect((uwlim-lwlim)/(maxunluck-minunluck),adjustable='box')

plt.xlabel('Money', **csfont)
plt.ylabel('No. unlucky events', **csfont)

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('MoneyUnLuckFucntion_SQUARE.png',dpi=100,facecolor='white',bbox_inches='tight')

plt.gca().set_aspect((uwlim-lwlim)/(2.5*(maxunluck-minunluck)),adjustable='box')
plt.savefig('MoneyUnLuckFucntion_PAPER.png',dpi=100,facecolor='white',bbox_inches='tight')




# Figure 7 - Histogram of talent, just for the sake of it. Figure 2 in the
# paper.
print('Plotting figure 7')
plt.figure(7)
binwidth = 0.01

plt.hist(talent, bins=arange(0,1.0,binwidth), color='red', histtype='stepfilled')

plt.xlabel('Talent', **csfont)
plt.ylabel('Number',**csfont)
ax = plt.gca()

# Get x and y ranges to set aspect ratio
minx = ax.get_xlim()[0]
maxx = ax.get_xlim()[1]
miny = ax.get_ylim()[0]
maxy = ax.get_ylim()[1]

plt.gca().set_aspect((maxx-minx)/(1.92*(maxy-miny)),adjustable='box')

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('TalentHistogram_PAPER.png')

# Square aspect ratio
plt.gca().set_aspect((maxx-minx)/((maxy-miny)),adjustable='box')

plt.savefig('TalentHistogram_SQUARE.png',dpi=100,facecolor='white',bbox_inches='tight')



# Figure 8 - plot the change in wealth over time for the wealthiest person
# The [0][0] is because "where" returns an array of arrays so there can be
# ambiguity if there are multiple agents with the same wealth.
print('Plotting figure 8')
agn = numpy.where(wealthlist >= numpy.max(wealthlist))[0][0]
plt.figure(8)
agentslist = numpy.asarray(agent[agn].tl)
t = agentslist[:,0]
l = agentslist[:,1]
m = agentslist[:,2]

plt.plot(t,m,color='black')

plt.xlabel('Time', **csfont)
plt.ylabel('Wealth',**csfont)
ax = plt.gca()

# Get x and y ranges to set aspect ratio
minx = ax.get_xlim()[0]
maxx = ax.get_xlim()[1]
miny = ax.get_ylim()[0]
maxy = ax.get_ylim()[1]

ax.set_title('Wealthiest individual '+str(agn)+', talent ='+str('%.2f' % agent[agn].Talent))

plt.gca().set_aspect((maxx-minx)/(maxy-miny),adjustable='box')

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('LuckiestHistory.png',dpi=100,facecolor='white',bbox_inches='tight')



# Figure 9 - change in wealth over time for the unluckiest person
print('Plotting figure 9')
agn = numpy.where(wealthlist <= numpy.min(wealthlist))[0][0]
plt.figure(9)
agentslist = numpy.asarray(agent[agn].tl)
t = agentslist[:,0]
l = agentslist[:,1]
m = agentslist[:,2]

plt.plot(t,m,color='black')

plt.xlabel('Time', **csfont)
plt.ylabel('Wealth',**csfont)
ax = plt.gca()

# Get x and y ranges to set aspect ratio
minx = ax.get_xlim()[0]
maxx = ax.get_xlim()[1]
miny = ax.get_ylim()[0]
maxy = ax.get_ylim()[1]

ax.set_title('Poorest individual '+str(agn)+', talent ='+str('%.2f' % agent[agn].Talent))

plt.gca().set_aspect((maxx-minx)/(maxy-miny),adjustable='box')

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('UnluckiestHistory.png',dpi=100,facecolor='white',bbox_inches='tight')



# Figure 10 - Pareto's rule as a function of time, e.g. the fraction of the
# total wealth owned by the top 20% richest people
print('Plotting figure 10')
plt.figure(10)

# Wealth of wealthiest people
plt.plot(pareto[:,0],pareto[:,1],color='black')

# Wealth of most talented people
plt.plot(pareto[:,0],pareto[:,2],color='red')

plt.xlabel('Time', **csfont)
plt.ylabel('Wealth fraction of top 20%')

plt.title('Black = wealthiest, red = most talented')

ax = plt.gca()
# Y-axis is percentage so has a fixed range by definition
ax.set_ylim([0.0,100.0])	

# X-axis is time so runs from 0 to maxt, the maximum time step
minx = 0.0
maxx = maxt

plt.gca().set_aspect((maxx-minx)/100.0,adjustable='box')

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('Pareto.png',dpi=100,facecolor='white',bbox_inches='tight')



# But the Pareto plot is the wealth of the 20% richest at any given time.
# It would be useful to show the varying wealth of the 20% richest people at
# the final timestep. Is it the case that the rich inevitably get richer, or
# does their wealth vary more haphazardly ?
# Get a list of the 20% richest agents at the end
# Go through every agent in the list. From their data, find their total wealth
# and thus get the total wealth fraction.
# This procedure also lets us calculate the total number of lucky events that befall
# the richest people. This is useful for comparing different methods.

# Create an array of the agents and their money at the end, which we can
# sort later to find those with the most wealth
allagents = numpy.zeros((nagents,3))

for i in range(nagents):
   allagents[i,0] = i
   allagents[i,1] = agent[i].Money
   allagents[i,2] = agent[i].NLucky
	
# Sort the array by current (final) money
allagents = allagents[allagents[:,1].argsort()]

# Extract an array of agent numbers of the end wealthiest people. This is used later. 
richlist = allagents[nagents-fifthfrac:nagents,0]

# Find the total number of lucky events that happened to the richest people
totalnlucky = 0.0
for i in richlist:
	totalnlucky = totalnlucky + agent[int(i)].NLucky

print('The total number of lucky events experienced by the richest people was ',totalnlucky)

# Before we deal with the richest, get the total wealth as a function of time
totalmoney = numpy.zeros((int(maxt/tstep),2))
# Iterate over time
for i in range(0,int(maxt/tstep)):
	currentmoney = 0.0
	# Iterate over agents
	for j in range(nagents):
		currentmoney = currentmoney + agent[j].tl[i][2]
	totalmoney[i,0] = float(i)*tstep
	totalmoney[i,1] = currentmoney



# Plot total money as a function of time
print('Plotting figure 11')
plt.figure(11)

plt.plot(totalmoney[:,0],totalmoney[:,1],color='black')

plt.xlabel('Time', **csfont)
plt.ylabel('Total money')

ax = plt.gca()
ax.set_ylim([AgentMoney*nagents,numpy.max(totalmoney[:,1])])	

# X-axis is time so runs from 0 to maxt, the maximum time step
ax.set_xlim([0.0,maxt])

plt.gca().set_aspect(maxt/(numpy.max(totalmoney[:,1])-AgentMoney*nagents),adjustable='box')

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('MoneyTime.png',dpi=100,facecolor='white',bbox_inches='tight')



# Plot total wealth of richest 20% at the end
totalmoney = numpy.zeros((int(maxt/tstep),2))
# Iterate over time
for i in range(0,int(maxt/tstep)):
	currentmoney = 0.0
	# Iterate over agents
	for j in richlist:
		j = int(j)
		currentmoney = currentmoney + agent[j].tl[i][2]
	totalmoney[i,0] = float(i)*tstep
	totalmoney[i,1] = currentmoney

print('Plotting figure 12')
plt.figure(12)

# Wealth of wealthiest people
plt.plot(totalmoney[:,0],totalmoney[:,1],color='black')

plt.xlabel('Time', **csfont)
plt.ylabel('Total money')

ymin = numpy.min(totalmoney[:,1])
ymax = numpy.max(totalmoney[:,1])

ax = plt.gca()
ax.set_ylim([ymin,ymax])	

# X-axis is time so runs from 0 to maxt, the maximum time step
ax.set_xlim([0.0,maxt])

plt.gca().set_aspect(maxt/(ymax-ymin),adjustable='box')

plt.tick_params(labelsize=axesnumbrsize)

ax.set_title('Wealth of 20% richest people at the end')

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('RichMoneyTime.png',dpi=100,facecolor='white',bbox_inches='tight')



# Plot wealth histories of final richest 20%
print('Plotting figure 13')
plt.figure(13)

for i in richlist:
	i = int(i)
	richarray = numpy.asarray(agent[i].tl)
	plt.plot(richarray[:,0],richarray[:,2])

ax = plt.gca()
miny = ax.get_ylim()[0]
maxy = ax.get_ylim()[1]
minx = ax.get_xlim()[0]
maxx = ax.get_xlim()[1]

plt.xlabel('Time', **csfont)
plt.ylabel('Money')

plt.gca().set_aspect((maxx-minx)/(maxy-miny),adjustable='box')

plt.tick_params(labelsize=axesnumbrsize)

plt.savefig('RichMoneyPlots.png',dpi=100,facecolor='white',bbox_inches='tight')




# Ideas to test :
# Before doing anything more ambitious, we should see how this result is
# to the method used. E.g. :
# - How much does the wealth distribution change if we let it go on for
# longer ?
# - There is only one generation here, in reality there are many. Simple
# way to include this is to (retroactively) consider each time step of
# each agent as that of a separate generation and compute the final
# wealth from the sum of all of these. This assumes each generation is
# completely independent of the others, which fits well with the spirit
# of the original proposal. More sophisticated approach would be to run
# different simulations and combine their parallel results.
# - Earlier testing found results are very sensitive to model. What if
# events randomly flip between being good and bad ? At each timestep,
# flip luck value of every event.
# - Agents are fixed while events move. What happens if agents also move ?
# - If we simply decrease the timestep we should get an identical result
# but in a shorter absolute time. What if we also decrease the amount by
# which events move ?
# - What if the initial distribution of wealth isn't uniform ? There's
# no particular reason to expect a wealthy individual to stay wealthy
# in this model. Could start with the distribution from the end of a 
# simulation. In principle this should give identical results to a longer
# run, unless the "random" nature of the walk is important !
# - What if talent lets you dodge misfortune as well as exploit luck ?
# - What if amount of money gained/lost in each event depends talent,
# rather than talent just determining probability of doubling/halving ?
# - What if talent changes ? Experience is a powerful teacher !
# - What if events are simply moved to some random new location rather
# than walking there ?
# - May be useful to visualse lucky/unlucky strikes, so can can compare
# how different motion distributions change frequency.
# - What if events aren't given any intrinsic luck status, but it's entirely
# due to the talent of people to exploit them in good or bad ways ? Luck is
# partially but surely not entirely subjective - if you're eaten by a lion 
# that surely can't be considered lucky except in REALLY extreme 
# circumstances...
# - In this model, wealth can be created and destroyed freely, but it is
# never lost or transferred. Wealth can be reduced but no-one goes into
# debt. Thus there is no restriction on the maximum possible wealth but
# a hard limit of zero on the lower limit. Is this the origin of the
# power law ? 
# - Plot wealth fraction of 20% most talented !!!!
# - Have luck status of event depend on agent's talent rather than be
# intrinsic, basically modelling inspiration particles
# Talent is something that is generally understood to be skill-specific. But
# how specific ? Being good at science is no good at being a good science
# communictor, nor is it even a prerequisite. So more realistically people
# have many talents. They can be skilled at some things and not at others.
# Could it be that people are unfairly rewarded because they are skilled at
# something that sounds similar to what they do, but isn't really. E.g.
# science communication isn't the same as actual research. Could perhaps get
# a highly paid job on the specious basis of similar btut actually unrelated
# abilities.

# ... and all that's before we even start to consider making things more
# realistic ! More ambitious goals :
# - People should constantly accumulate wealth in direct proportion to
# their talent as well as due to luck. In reality people generally have
# reliable salaries, after all.
# - Multiple generations should be done self-consistently, who begin with
# capital depending on their parent's wealth (which should also decrease)
# their parents wealth
# - Different ways of altering talent through time. In reality, it might
# increase if individuals do the same job constantly, but fluctuate if
# they change jobs.
# - Many jobs have salary increases/decreases which are based on simple
# experience and loyalty (time spent in job) rather than skill at that
# job.
# - Salaries do not necessarily match up with talent at all.
# - Could be that salaries obey a power law even though talent is Gaussian.
# Not necessary that talent gives you and special ability to gain higher
# rewards, it's that society might deem a slightly higher talent level to be
# worth a lot more.
# - Probably hopelessly ambitious but what about including a UBI ?
# - Maybe a sensible, achievable goal for part two would simply be to
# demonstrate that the same kind of wealth distribution can in principle
# arise from talent instead of luck. The authors didn't demonstrate that
# abilities really do follow a Gaussian distribution. It could also be
# that desires and talent are different, that a relatively small fraction
# have both the ability and ambition to achieve success. Rather than the
# existence of a few obscenely wealthy individuals, the problem may be
# more that most people with moderate levels of talent and ambition have
# far less success than those would naively suggest.
# - Include an ambition parameter ?
# - Ability to exploit talent may not be linearly dependent on talent
# itself.
# - Charity
# ... and many more, no doubt.

# Better comparisons to observations. Should be able to get statistical data
# for physical characteristics and abilities, e.g. running speed, strength etc.
# Could get mental data from IQ and Berlin numeracy tests, maybe more
# Measuring skill at a particular role might be impossible ! How do you say
# if someone is really good at something or just popular ?


# DEPRECATED. Old code, test methods of how agents encounter lucky events
#        # Method 1. Use pure chance to decide if each agent experiences
#				 #	 an event or not at this timestep.
#        event = random.random()
#        
#        # If the event number is < 0.333, let nothing happen.
#        
#        # If the event number is 0.333 - 0.666, let the event be 
#				 # unlucky.
#        if (event >= 0.333) and (event <= 0.666):
#            agent[i].Money = agent[i].Money / 2.0
#            
#        # If the event number is > 0.666, let the event be lucky. While
#				 # talent in this model doesn't affect bad luck, the chance to
#				 # profit depends on talent as well as luck. So we need a second
#		     # random number :
#        if (event > 0.666):
#            Tnumber = random.random()
#            if Tnumber < agent[i].Talent:
#                agent[i].Money = agent[i].Money * 2.0


#       # Method 2.  Maybe a better approximation to the paper which 
#       # uses a random walk. 
#       # A person must be within a circular event patch of radius 1.0
#	      # for something to occur. The area is 201*201 patches. So chance 
#       # of an event is pi / (201*201). There are 500 event patches 
#       # with a totally random distribution, so run this 500 times per
#       # agent (events can overlap in position with each other; still 
#       # I'm sure there's an easier way to calculate this, but meh).
#       # Some kind of event will happen if a random number is less than
#       # the event number
#        echance =  pi / (201.0*201.0)
#        for j in range(neventob):
#            enumber = random.random()
#            if enumber <= echance:
#                nevents = nevents + 1
#                
#                # Another random number to decide if the event is good
#                # or bad
#                etype = random.random()
#                
#                # Good event case. Whether the agent benefits or not 
#                # depends on chance in proportion to their talent.             
#                if etype >= 0.5:
#                    ngood = ngood + 1
#                    Tnumber = random.random()
#                    if Tnumber < agent[i].Talent:
#                        agent[i].Money = agent[i].Money * 2.0
#                        agent[i].NLucky = agent[i].NLucky + 1
#                        nsuccess = nsuccess + 1
#                
#                # Unlucky event case. Talent doesn't help them deal 
#                # with these.    
#                if etype < 0.5:
#                    agent[i].Money = agent[i].Money / 2.0
#                    agent[i].NUnlucky = agent[i].NUnlucky + 1
#                    nbad = nbad + 1


# Working notes on conclusions :
# FIDUCIAL RUN
# Standard :
# - Reproduces the Pluchino result very well. Additonally, note that Pareto
# rule varies as a function of time, and is not reached until the end of the
# 40 year period. 20% most talented individuals own about 20% of the wealth.
# There is truly no correlation between talent and money. 
# It also seems that the rich get richer : there's not much mobility here.
# The wealth of those who become the richest 20% pretty much continually
# increases over time. This is because wealth can only double or halve. If
# someone has a series of unlucky events, they need to have that many lucky
# events occur to restore their former fortunes. Mobility into the 20% is
# only really possible if someone avoids many events and then suddenly has
# a good run.
# Note how different this is from real events, which can increase wealth
# completely independently of starting wealth (e.g. a lottery win is small
# for a millionaire but huge for someone with no money at all)
# 1000 events :
# - Alters the slope of the money distribution and the 80:20 rule is reached
# very quickly, and by the end of the simulation the 20% richest own nearly
# all of the wealth.
# 2000 agents :
# - Doesn't really change anything.
# Agents and events both moving :
# - Doesn't really change anything.
# Events changing status randomly :
# - Doesn't really change anything.
# Events moving at a different speed :
# - Significant effects. Slower speed = more rapid inequality; by the end
# 20% own almost 100%; shallower power law. Faster speed = slower inequality
# development; power law same as standard.

# ALTERING WHAT TALENT DOES
# Allowing talent to also reduce probability of negative events :
# - Causes a weak but visible correlation between talent and wealth. Also
# the wealth fraction of the 20% most talented rises, but only by a few %.
# Allowing talent to set luck status of events :
# - Causes a distinct and clear correlation between wealth and talent,
# albeit highly scattered. Wealth fraction of 20% most talented rises 
# steadily to 40-50% by end. Power law slope is as standard.
# Allowing talent to do both of the above :
# - As above but with somewhat less scatter.
# Allowing talent to set how much influence successful events have :

# ALTERING TALENT DISTRIBUTION
# Using fiducial model but with uniform random talent :
# - High scatter but a very strong trend. This suggests the apparent lack of
# corellation in the fiducial run is only because there were very few talented
# people so it was difficult to discern (the reason the wealthiest and poorest
# individuals tend to have similar talents is because with a Gaussian
# distribution, ALL individuals tend to have similar talents - with a uniform
# distribution the difference is much larger). Again the slope of the money 
# distribution is the same as the fiducial. 
# Fiducial model but with talent affecting event status and mitigating
# unfortunate events, and uniform random talent distribtuion :
# - Again very strong correlation of talent/money, maybe with less scatter.
# Slope of money distrubution remains similar though it's shallower.

# Would it be faster to do the events by another method like in 
# the old code ? E.g. get the distribution of events for all
# agents at each timestep. Then can ensure everyone experiences
# about the same number of events as in the random-walk method
# (for example calculate how many events should happen, then
# randomly select that many agents - could make an equal number
# bad and good, or have the agents determine it)
# - This is working but the results don't quite match, e.g. slope of wealth
# distribution is wrong. Possibly the EFD is not quite a Gaussian with the
# random walk method.

# The attempt at a faster method revealed that the position of the agents is
# very different over time because of the random-walk. The events are moving
# sufficiently slowly that although there position at any timestep can be
# regarded as random in space, it is NOT random in relation to the adjoining
# timesteps. So events hang around in preferential locations, bringing more
# good and bad luck to certain areas than to others. Even though the overall
# event occurrence distribution matches a random approximation, the repeat rate
# does not.
# This suggests that geography has become a key factor (a postcode lottery !).
# So instead of plotting overal trend of talent-luck, we should actually plot it
# for similar areas, i.e. for agents within some radius. Could try making a 
# "wealth map" to choose which areas to plot.

# Or maybe not about geography but no. events experienced. There may not be any
# trend overally, but still be trends of money with talent for agents of similar
# numbers of events.
# ... this indeed the case ! Albeit only weakly. If we do the wealth-talent plots
# for agents who experience similar numbers of events (both kinds), then it's hard
# to visually see the trends but the linear regression fits give clearly different
# slopes : the more events, the greater the influence of talent on wealth.
# This might be more visible if we use more agents since we should have more agents
# in each event bin. Also using a uniform distribution of talent, otherwise the
# extemes will be underpopulated.
# So it's also the combination of people with multiple different talents that
# disguises the influence of talent itself. The trends are weak, and luck IS
# dominant, but the appearance of talent having no effect at all is more due
# to scatter disguising its influence.
# We could also see if there's a similar trend for those of similar wealth levels...

# Claims that luck matters more than talent in terms of starting wealth being
# a bigger factor than genetics (https://plus.google.com/u/0/+RhysTaylorRhysy/posts/HdZD1FMBn4K). Can this
# be approximated here ?
# Quick attempt was to simply randomise event distribution at a selected time.
# Thus agents would now experience a different set of events while having a
# different wealth distribution. However this wealth distribution may not be
# independent of talent (especially see above), so it would be better to change
# the initial wealth distribution and comparing the results, rather than
# varying things halfway through.
# In principle I can see the model supporting this result, since if the starting
# wealth is extremely low, many more lucky events will be needed to reach any
# given wealth level. And that will only happen in certain areas.
Python 3.6.1 (default, Dec 2015, 13:05:11) [GCC 4.8.2] on linux