intvnut Posted April 30, 2012 Share Posted April 30, 2012 (edited) IntroductionOk, so I spent a couple days reverse engineering Utopia. I now know pretty much how it all works, and now you can find out too.Here's some highlights, in case you don't want to wade through the code: Forts: Do protect a 1 unit radius against rebels Will protect parked ships that have the same owner as the fort. Radius of 1 card. Will protect both players ships when they're under active control. Radius of 1 card. Do not otherwise contribute to your score Bugs: Scoring will overflow, if you have more than 65 crops + fishing boats combined. It will also overflow if you earn more than 255 gold bars in a single turn. (Is that possible?) The "float off bottom of screen" bug is likely due to island/status collision detection interacting poorly with EXEC's boundary detection. The game can "nudge" a boat across the boundary the EXEC checks for. There is a check for this, but it only has been applied properly to the left/right edges. Delay loop in scoring display appear to want to update random number generator, but they don't actually. Code quality:Let's just say it's likely the priority was to have a compelling game, and have it out quickly, as opposed to writing the tightest, cleanest possible code. Miscellaneous: Pirates will never sail toward a parked PT boat on purpose. The "nudge" code, though, can nudge them through a tangle of PT boats. Hurricanes are 5 times more destructive than tropical storms. All three forms of weather water crops at the same rate, though. There's two copies of the "sinking fishing boat" animation in the ROM, due to how the EXEC distinguishes between background cards and MOB animations Each island has precisely 29 squares. When weather takes out something on your island, it will incur 0 to 101 casualties. If you want more details, you'll have to dig through the code unless it's covered in the slightly more detailed review below. :-) Code attached.Code StructureThe bulk of the main game code can be put into one of three categories -- The Timer Tick Task, Dispatches, Scoring Logic. The rest of the code is either initialization code, or support code for those three categories.Timer Tick TaskThe bulk of the game logic hangs off the the Timer Tick Task. This task runs 20 times a second, and it handles everything from spawning weather to collision detection to animating certain sinking ships. It's quite a lot of code and is fairly linear. The rough order of execution: Update the weather, possibly creating new weather Update the fish, possibly creating new schools of fish Update the pirates, possibly spawning new pirates Island vs. boat collision detection Update the game clock If not end of round / end of game: Update the status line at bottom of screen Update parked sinking ships At end-of-round, fire off the scoring code and then show the scores At end-of-game, show final score and halt DispatchesThe next big set of code is the set of Dispatches. The EXEC mostly works by calling various functions in response to various events. Dispatches fall into a few categories: Controller input dispatches. The action-button and disc handlers are pretty simple. The keypad dispatch is amazingly convoluted, but that makes sense when you consider everything that it has to account for. Object vs. object dispatches. These handle pirates vs. PT boats and similar such things. Object vs. land dispatches. This is what handles rain on crops, pirates sinking parked ships, or parked ships going fishing. Scoring LogicThe final big piece of code is the scoring logic. Scoring happens in four phases: Income Computation, Population Update, Round Score Calculation, Rebellion. Income Computation During the round, every gold bar you earn (say due to fishing, rain on crops, etc.) gets tallied in this round's Gross Domestic Product (Round GDP), separate from your actual gold bar balance. That is, spending does not subtract from Round GDP even though it lowers your treasury balance. At the end of the round, you get awarded additional gold as follows. Each of these contributes to the Round GDP except for the "baseline 10 bars." 4 gold bars per factory 1 gold bar per fishing boat Productivity bonus: ((Schools + Hospitals) * Factories) + Hospitals, clamped to a maximum of 30 gold bars. 10 gold bars of baseline income (does not contribute to the Round GDP). Population computation -- expressed as a growth rate, resulting in exponential growth. Fertility computation Start with a baseline fertility rate of 5.0% Increase fertility by 0.3% for every crop Increase fertility by 0.3% for every hospital Increase fertility by 0.1% for every house Decrease fertility by 0.3% for every school Clamp fertility to a minimum of 4.0%. You can't have fertility below 4% even if you filled the island with schools. Mortality computation Start with a baseline mortality rate of 1.1% Decrease mortality by 0.3% for every hospital, but not below 0.2%. (This limit is applied before the next step.) Increase mortality by 0.1% for every factory. If you fill the island with factories, your mortality rate will be 4.0%, matching the minimum allowed fertility. New population: Population + Population * Fertility - Population * Mortality. Maximum allowed population is limited to 9999. Round Score Calculation -- roughly, "approval rating", 0-100% First compute the following four subscores: Housing score: ((Houses * 500) / (Population / 100)) / 3. If larger than 30, clamp it to 30. Per-capita GDP score: ((Round GDP * 100) / (Population / 100)) / 12. If larger than 30, clamp it at 30. Food supply score: (((Fishing boats + Crops) * 500) / (Population / 100)) / 3. If this value is larger than 30, clamp it to 30.Note: 65 * 500 = 32500. So, if you have more than 65 fishing boats + crops, this score can go negative. This is fixable by changing the BLE at $5B1E to a BNC, I think, so it treats the overflow case as a case that needs to clamp to 30. General welfare score: 1 point for every school or hospital Add up the four subscores, limiting the total to 100 or less. That's the per-round score. Rebellion Compare this round's score to the previous round, and consider it in absolute terms as well If it dropped by more than 10 points or is below 30 points, add a rebel If it increased by more than 10 points or is above 70 points, remove a rebel If you want to see the exact details of how these pieces work, look at the code. For example, in the scoring section, some divides are rounding divides, and some are truncating divides. The population computation is actually carried out with scaled arithmetic (ie. fertility/mortality rates are multiplied by 10, and population divided by 10 when computing numbers of births and deaths.)For anything else... see the code! ____ [Edited to fix some formatting issues in the nested bulleted lists.] utopia.asm Edited August 3, 2018 by intvnut 6 1 Quote Link to comment Share on other sites More sharing options...
+thegoldenband Posted April 30, 2012 Share Posted April 30, 2012 Joe, thank you so much for this! There are already some fascinating revelations, like the fact that schools decrease fertility. I'm very much looking forward to taking a browse through your disassembly; a lot of it will go over my head, but here's hoping some doesn't. I assume this also puts to bed the notion that there's any hidden in-game content or "governor's award". I didn't think so, but a part of me held out hope. Quote Link to comment Share on other sites More sharing options...
intvnut Posted April 30, 2012 Author Share Posted April 30, 2012 Joe, thank you so much for this! There are already some fascinating revelations, like the fact that schools decrease fertility. I'm very much looking forward to taking a browse through your disassembly; a lot of it will go over my head, but here's hoping some doesn't. I assume this also puts to bed the notion that there's any hidden in-game content or "governor's award". I didn't think so, but a part of me held out hope. Yep, the "Governor's Award" is just a fiction of the documentation, I suppose. If you make it to the end of the game with a reasonably successful island, you can award it to yourself. You know if you've succeeded or not, after all. The only thing even remotely hidden in the ROM is a copyright notice, and a couple pieces of dead code. Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted April 30, 2012 Share Posted April 30, 2012 Fantastic, Joe! Now, for the question on everybody's mind... Is this just a prelude to working on Utopia 2, or a souped up version that runs at 60 Hz? -dZ. Quote Link to comment Share on other sites More sharing options...
intvnut Posted April 30, 2012 Author Share Posted April 30, 2012 Fantastic, Joe! Now, for the question on everybody's mind... Is this just a prelude to working on Utopia 2, or a souped up version that runs at 60 Hz? -dZ. Well, I can't answer for anyone else. I can say this: I certainly am not working on either a 60Hz Utopia or a Utopia 2. That said, if somebody does, I'd like to get a copy! 1 Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted April 30, 2012 Share Posted April 30, 2012 Fantastic, Joe! Now, for the question on everybody's mind... Is this just a prelude to working on Utopia 2, or a souped up version that runs at 60 Hz? -dZ. Well, I can't answer for anyone else. I can say this: I certainly am not working on either a 60Hz Utopia or a Utopia 2. That said, if somebody does, I'd like to get a copy! Well, I was hoping. Perhaps that can be a test case when the new version of P-Machinery is ready? It certainly does not look as demanding (graphically), except for the back-end logic of the simulation--but you have de-mystified that. I can imagine the various stages of the game and the event-driven nature of game-play lend themselves well to P-Machinery's abilities. -dZ. Quote Link to comment Share on other sites More sharing options...
Pixelboy Posted April 30, 2012 Share Posted April 30, 2012 My friend, you just earned yourself a spot in the credits section of the manual of the ColecoVision version. Thank you SOOOOO much for doing this! Utopia is a game that I want to code myself for the CV eventually, but it will take a couple of years, because I want to develop my BasicVision language and IDE first (in 2013). If it all works out, I'll code Utopia with BasicVision in 2014. Until then, I'll keep the data in the first post (as well as your disassembly file) in a safe spot in my records. Thanks again! 1 Quote Link to comment Share on other sites More sharing options...
Ransom Posted April 30, 2012 Share Posted April 30, 2012 Awesome! Thank you very, very much! Quote Link to comment Share on other sites More sharing options...
+cmart604 Posted April 30, 2012 Share Posted April 30, 2012 My friend, you just earned yourself a spot in the credits section of the manual of the ColecoVision version. Thank you SOOOOO much for doing this! Utopia is a game that I want to code myself for the CV eventually, but it will take a couple of years, because I want to develop my BasicVision language and IDE first (in 2013). If it all works out, I'll code Utopia with BasicVision in 2014. Until then, I'll keep the data in the first post (as well as your disassembly file) in a safe spot in my records. Thanks again! Awesome! I love seeing my two favourite consoles come together! Quote Link to comment Share on other sites More sharing options...
Carl Mueller Jr Posted May 1, 2012 Share Posted May 1, 2012 Fantastic, Joe! Now, for the question on everybody's mind... Is this just a prelude to working on Utopia 2, or a souped up version that runs at 60 Hz? -dZ. Well, I can't answer for anyone else. I can say this: I certainly am not working on either a 60Hz Utopia or a Utopia 2. That said, if somebody does, I'd like to get a copy! I would be willing to do it if it can be sanctioned by Intellivision Productions, which I strongly doubt. I also would not recommend anyone else attempting a port without their permission. 1 Quote Link to comment Share on other sites More sharing options...
BillyHW Posted May 1, 2012 Share Posted May 1, 2012 Someone needs to code a 1-player version with an AI player 2. 1 Quote Link to comment Share on other sites More sharing options...
intvnut Posted December 27, 2016 Author Share Posted December 27, 2016 In this question in another thread, dZ recently asked the following question, which was inspired by the Utopia graphics chart that had been displayed at ICHEG some time back. Since you reversed-engineered the code, can you confirm that the outer edge is not in the map itself made invisible with the background colour? I know you disagreed before, but it seems plausible to me that they could mark some "special zone" that could be detected with hardware collision detection for the position of fish schools or pirate ships. It could also mark the range of the fort protection on your fishing ship. The 1 card zone around the border of the islands does not contain special cards for hardware collision detection purposes, such as indicating the zone potentially protected by a fort. Utopia uses a single method for determining whether a MOB or a card is in a 1 unit radius of a fort: Given a card position on the map, it looks through a 1 card radius of that position to see if the card contains a fort graphic. For MOBs, it first converts the MOB coordinate to a card position. The code which performs both checks looks like this: . ;; ---------------------------------------------------------------- ;; ;; Look for a fort within 1 square of a MOB. ;; ;; ---------------------------------------------------------------- ;; FORT_NEAR_MOB: L_564A: PSHR R5 ; 564A PSHR R1 ; 564B JSR R5, MOB_TO_CARD ; 564C Convert MOB position to card posn L_564F: CLRR R0 ; 564F First search positive offsets L_5650: SDBD ; 5650 \__ Point to neighbor offset table MVII #NBR_TBL, R4 ; 5651 / L_5654: MVI@ R4, R1 ; 5654 Get neighbor offset TSTR R0 ; 5655 \__ Add or subtract? BNEQ L_565B ; 5656 / ADDR R2, R1 ; 5658 \__ R0 = 0: Add neighbor offset B L_565F ; 5659 / L_565B: PSHR R2 ; 565B \ SUBR R1, R2 ; 565C |_ R0 = 1: Sub neighbor offset MOVR R2, R1 ; 565D | PULR R2 ; 565E / L_565F: MVI@ R1, R3 ; 565F Get card SLR R3, 2 ; 5660 \ SLR R3, 1 ; 5661 |_ Is it a fort? ANDI #$00FF, R3 ; 5662 | CMPI #$0001, R3 ; 5664 / BEQ L_567A ; 5666 SDBD ; 5668 \__ Are we at end of table? CMPI #NBR_TBL+4, R4 ; 5669 / BEQ L_5670 ; 566C Yes: Go around outer loop B L_5654 ; 566E No: Get next offset. L_5670: TSTR R0 ; 5670 \_ We did both pos and neg? BNEQ L_5677 ; 5671 / Yes: Leave, no fort found. MVII #$0001, R0 ; 5673 \_ Do negative offsets B L_5650 ; 5675 / L_5677: CLRR R1 ; 5677 \ PULR R2 ; 5678 |- No fort found. Leave. PULR R7 ; 5679 / L_567A: MVI@ R1, R3 ; 567A \ SLLC R3, 2 ; 567B |- Whose fort is it? CURPLR BOV L_5686 ; 567C / or other player? MVII #$0001, R3 ; 567E Player 1's fort L_5680: MVO R3, CURPLR ; 5680 Remember which player's fort MVII #$0001, R1 ; 5682 We found a fort! PULR R2 ; 5684 \_ return PULR R7 ; 5685 / L_5686: CLRR R3 ; 5686 Player 0's fort B L_5680 ; 5687 ; Offsets for looking around a square for a fort. ; +1, +19, +20, +21 => E, SW, S, SE ; -1, -19, -20, -21 => W, NE, N, NW NBR_TBL: DECLE $0001, $0013, $0014, $0015 ; 5689 0001 0013 0014 0015 ;; ---------------------------------------------------------------- ;; ;; Look for a fort within 1 square of a card. ;; ;; Reuses code above for fort within 1 square of MOB. ;; ;; ---------------------------------------------------------------- ;; FORT_NEAR_CARD: L_568D: PSHR R5 ; 568D PSHR R1 ; 568E B L_564F ; 568F . As for the island graphics themselves, nothing special is stored in the cards around the border of the island. The islands are both represented as a list of card offsets: . ;; List of display offsets for the left and right islands, ;; followed by GRAM card indices associated with these positions. ;; (Ref: code @ 50D0 - 50F1, and subrtn at 511D - 5124) ;; ;; $512F - $514B $514C - $5168 ;; .................... .................... ;; .................... .................... ;; ..#................. ..........##.#..#... ;; .###................ ..........#######... ;; .###................ ..........##..####.. ;; .#####.............. ...............####. ;; ...####............. ................###. ;; ....#####........... ...............####. ;; ...###.#####........ .................#.. ;; .................... .................... ;; .................... .................... ;; .................... .................... LFT_ISLE_OFS_TBL: DECLE $002A, $003D, $003E, $003F ; 512F 002A 003D 003E 003F DECLE $0051, $0052, $0053, $0065 ; 5133 0051 0052 0053 0065 DECLE $0066, $0067, $0068, $0069 ; 5137 0066 0067 0068 0069 DECLE $007B, $007C, $007D, $007E ; 513B 007B 007C 007D 007E DECLE $0090, $0091, $0092, $0093 ; 513F 0090 0091 0092 0093 DECLE $0094, $00A3, $00A4, $00A5 ; 5143 0094 00A3 00A4 00A5 DECLE $00A7, $00A8, $00A9, $00AA ; 5147 00A7 00A8 00A9 00AA DECLE $00AB ; 514B 00AB RGT_ISLE_OFS_TBL: DECLE $0032, $0033, $0035, $0038 ; 514C 0032 0033 0035 0038 DECLE $0046, $0047, $0048, $0049 ; 5150 0046 0047 0048 0049 DECLE $004A, $004B, $004C, $005A ; 5154 004A 004B 004C 005A DECLE $005B, $005E, $005F, $0060 ; 5158 005B 005E 005F 0060 DECLE $0061, $0073, $0074, $0075 ; 515C 0061 0073 0074 0075 DECLE $0076, $0088, $0089, $008A ; 5160 0076 0088 0089 008A DECLE $009B, $009C, $009D, $009E ; 5164 009B 009C 009D 009E DECLE $00B1 ; 5168 00B1 LFT_ISLE_PIC_TBL: DECLE $0013, $0014, $0000 ; 5169 0013 0014 0000 DECLE $0015, $0017, $0000, $0018 ; 516C 0015 0017 0000 0018 DECLE $001D, $001E, $0000, $0020 ; 5170 001D 001E 0000 0020 DECLE $0015, $001D, $0000, $0000 ; 5174 0015 001D 0000 0000 DECLE $0015, $0017, $0000, $001E ; 5178 0015 0017 0000 001E DECLE $0020, $0015, $0019, $001E ; 517C 0020 0015 0019 001E DECLE $001F, $001D, $001E, $001C ; 5180 001F 001D 001E 001C DECLE $001B, $001A ; 5184 001B 001A RGT_ISLE_PIC_TBL: DECLE $0014, $0015 ; 5186 0014 0015 DECLE $0013, $0013, $0017, $0000 ; 5188 0013 0013 0017 0000 DECLE $001B, $001E, $001C, $0020 ; 518C 001B 001E 001C 0020 DECLE $0018, $001D, $001F, $001D ; 5190 0018 001D 001F 001D DECLE $0000, $0000, $0015, $001D ; 5194 0000 0000 0015 001D DECLE $0000, $0000, $0015, $0017 ; 5198 0000 0000 0015 0017 DECLE $0000, $0018, $0019, $001E ; 519C 0000 0018 0019 001E DECLE $0000, $001F, $0016 ; 51A0 0000 001F 0016 . No corresponding table exists for the boundary of either island. And finally, if you examine BACKTAB during the game, you can see that there is nothing special about the water cards outside the islands. If there were, then there would be non-zero BACKTAB entries in the leftmost and rightmost columns in particular. Rather, they're all zeros. If you look more closely, the only non-zero BACKTAB cards in the main map exactly correspond to the two sets of tables above. . 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 489B 0000 0000 0000 0000 0000 0000 0000 08A3 08AB 0000 089B 0000 0000 089B 0000 0000 0000 0000 48A3 4803 48AB 0000 0000 0000 0000 0000 0000 08BB 0803 08DB 08F3 08E3 0903 08C3 0000 0000 0000 0000 48BB 4803 48C3 0000 0000 0000 0000 0000 0000 08EB 08FB 0000 0000 08EB 0803 0803 08AB 0000 0000 0000 48EB 48F3 4803 4903 48AB 0000 0000 0000 0000 0000 0000 0000 0000 0000 08EB 0803 0803 08AB 0000 0000 0000 0000 48EB 4803 4803 48AB 0000 0000 0000 0000 0000 0000 0000 0000 0000 08BB 0803 08C3 0000 0000 0000 0000 0000 48BB 4803 48F3 4903 48AB 0000 0000 0000 0000 0000 0000 08CB 08F3 0803 08FB 0000 0000 0000 0000 48CB 48F3 48FB 0000 48EB 48F3 48E3 48DB 48D3 0000 0000 0000 0000 0000 08B3 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0004 008C 0084 0084 0000 0000 00AE 0086 0006 008E 008E 00BE 0000 0000 0002 008A 0082 0082 0000 . 1 Quote Link to comment Share on other sites More sharing options...
+DZ-Jay Posted December 27, 2016 Share Posted December 27, 2016 Cool! Thanks. Quote Link to comment Share on other sites More sharing options...
BBWW Posted December 28, 2016 Share Posted December 28, 2016 I'm waiting for Dystopia. 1 Quote Link to comment Share on other sites More sharing options...
irvinetom Posted October 28, 2018 Share Posted October 28, 2018 Amazing, amazing work! Thank you so much for this!!!! Quote Link to comment Share on other sites More sharing options...
PlanetJanus Posted May 29, 2019 Share Posted May 29, 2019 I'm waiting for Dystopia. Why wait? IntyBASIC is here. Quote Link to comment Share on other sites More sharing options...
PlanetJanus Posted May 29, 2019 Share Posted May 29, 2019 Utopia is great if you can find anyone else geeky enough to want to play with you. The Grandfather of most "NATION BUILDING" Games. Quote Link to comment Share on other sites More sharing options...
BiffMan Posted March 3, 2020 Share Posted March 3, 2020 Awesome thread and thanks very much for the insights on the calculations! I'll need to do some more experiments first, but doesn't seem like there is any minimum fertility rate, or at least it's not working as designed. With 5 factories and 24 schools, I was getting a steady -4.6% population loss every turn for the part I kept track of. Great to know that schools are more effective at population control than factories! Curious to know if there is anything in the code that would explain getting 100 scores a while after population goes negative. When it first goes negative, you lose all population-based sources of score and just get the schools+hospitals flat score, but after a few turns of 'adjustment period' where everyone gets used to being dead, scores jump to a consistent 100 every round. I tried it out today just to make sure my recollection was accurate and was able to reproduce it readily enough. It's not a viable highscore strategy or anything, but my friends and I used to play that way BITD as an amusing alternative. Endless fun rooting for storms to hit you, etc. ? Quote Link to comment Share on other sites More sharing options...
intvnut Posted March 3, 2020 Author Share Posted March 3, 2020 2 hours ago, BiffMan said: Awesome thread and thanks very much for the insights on the calculations! I'll need to do some more experiments first, but doesn't seem like there is any minimum fertility rate, or at least it's not working as designed. With 5 factories and 24 schools, I was getting a steady -4.6% population loss every turn for the part I kept track of. Great to know that schools are more effective at population control than factories! Curious to know if there is anything in the code that would explain getting 100 scores a while after population goes negative. When it first goes negative, you lose all population-based sources of score and just get the schools+hospitals flat score, but after a few turns of 'adjustment period' where everyone gets used to being dead, scores jump to a consistent 100 every round. I tried it out today just to make sure my recollection was accurate and was able to reproduce it readily enough. It's not a viable highscore strategy or anything, but my friends and I used to play that way BITD as an amusing alternative. Endless fun rooting for storms to hit you, etc. ? Hmm. Let me go take a look at the source. I probably made some errors. For example: MVII #$0028, R3 ; 5A40 Fertility baseline of 50 (5.0%) $28 is 40, not 50. So... clearly I need to review this. Quote Link to comment Share on other sites More sharing options...
intvnut Posted March 3, 2020 Author Share Posted March 3, 2020 2 hours ago, BiffMan said: Awesome thread and thanks very much for the insights on the calculations! I'll need to do some more experiments first, but doesn't seem like there is any minimum fertility rate, or at least it's not working as designed. With 5 factories and 24 schools, I was getting a steady -4.6% population loss every turn for the part I kept track of. OK, I looked at the code, and I apparently must have made a gigantic brain fart in the wee hours of the morning when I said there was a lower bound of 4% on the fertility. CMPI #$0040, R3 ; 5A6C \ BLE L_5A72 ; 5A6E |- Min fertility of 40. MVII #$0040, R3 ; 5A70 / L_5A72: MVI G_0324, R0 ; 5A72 Get pop/10 That comment is 100% bogus. Here's what it should say: CMPI #$0040, R3 ; 5A6C \ BLE L_5A72 ; 5A6E |- Max fertility of 64. MVII #$0040, R3 ; 5A70 / L_5A72: MVI G_0324, R0 ; 5A72 Get pop/10 That's right, it's a clamp on the upper bound of fertility, not the lower bound. *d'oh* 24 schools should be a -7.2% on bias on fertility. 4.0% - 7.2% = -3.2%. 5 factories should be 1.1% + 5 * 0.1% = 1.6% mortality. So, for a population of 1000, you should see 1000 - 3.2% *1000 - 1.6% * 1000 = 1000 - 32 - 16 = 952. A -4.6% would take 1000 down to 954. Can you double check your measurements? As for negative population driving 100 scores, I can see that. I need to investigate what the divide routine does when passed a negative divisor and/or dividend. It's possible one or more steps treat the negative number as a large positive number, giving an off-the-charts score. There are three score components that involve population, and those only add to 90. You'd need to get the other 10 points through schools or hospitals (general welfare score). 1 Quote Link to comment Share on other sites More sharing options...
BiffMan Posted March 4, 2020 Share Posted March 4, 2020 21 hours ago, intvnut said: OK, I looked at the code, and I apparently must have made a gigantic brain fart in the wee hours of the morning when I said there was a lower bound of 4% on the fertility. Can you double check your measurements? As for negative population driving 100 scores, I can see that. I need to investigate what the divide routine does when passed a negative divisor and/or dividend. It's possible one or more steps treat the negative number as a large positive number, giving an off-the-charts score. There are three score components that involve population, and those only add to 90. You'd need to get the other 10 points through schools or hospitals (general welfare score). Played again today with my daughter and confirmed the -.3% per school and -.1% per factory do work as advertised. Therefore building schools in the historical storm path is by far the most cost effective means of lowering population. Also means that 29 schools and a -5.8% (4% - 8.7% - 1.1%) population rate is the max. I did stumble onto a discovery in regards to the scoring glitch at low populations, which I'd love to know the coding behind... The score tanking happens exactly with a population -100<X<100 at which point only schools and hospitals count toward score, all population based ones give zero. Above 100 pop is normal scoring and below -100 pop gives max 100 score. Unknown if the clip is 99 or 100, but my daughter caught it going in and out and even managed to go to -101 for max score then to -99 the next turn for 19 points and a rebel. Definitely a threshold thing. Thanks again for all this great info, it's delightful to know schools are more effective than factories for growth suppression, fishing boats don't affect growth rate, and houses only increase by 1/3 the reduction from a school. Still left to the whims of the weather, but I think a min/max strategy of 2 fishing boats, 2 houses, 3-4 factories, and a crap ton of schools is quite viable. You'll score well in the short term and if you can get lucky and not linger in the 100 to -100 range too long, there's no significant downside to going negative. Fun! ? Quote Link to comment Share on other sites More sharing options...
intvnut Posted March 4, 2020 Author Share Posted March 4, 2020 (edited) I will definitely have to review my disassembly and make corrections. I'm not sure I followed this calculation: 1 hour ago, BiffMan said: Also means that 29 schools and a -5.8% (4% - 8.7% - 1.1%) population rate is the max. Where did the 1.1% come from? EDIT: Never mind: Baseline mortality. Edited March 4, 2020 by intvnut Realized my error. Quote Link to comment Share on other sites More sharing options...
intvnut Posted March 4, 2020 Author Share Posted March 4, 2020 2 hours ago, BiffMan said: The score tanking happens exactly with a population -100<X<100 at which point only schools and hospitals count toward score, all population based ones give zero. Above 100 pop is normal scoring and below -100 pop gives max 100 score. OK, I believe -100 and below should give you "max 100". Here's my reasoning: Population is stored as a 16-bit value. When computing the score, the code performs a non-rounding divide by 100: L_5ABD: MVII #POPU_0,R4 ; 5ABD \ JSR R5, L_5A29 ; 5ABF |_ Get player's (updated) SDBD ; 5AC2 | population count MVI@ R4, R1 ; 5AC3 / MVII #$0064, R2 ; 5AC4 \_ Pop / 100 JSR R5, X_DIV ; 5AC6 / I checked the divide code, and it appears to correctly account for the signs of both numerator and denominator. It remembers the sign of the numerator and denominator, flips them both positive, and then sets the sign of the result to the XOR of the two signs (effectively). So +/+ gives +, -/- gives +, and +/- or -/+ gives -. So, the divide above will clamp populations from -99 to 99 down to 0 for scoring, fitting your -100 < X < 100 criterion. For -100 and below, the computation above results in a negative value. The subsequent subscore computations use this negative value and compute negative scores. For example, this computes a negative housing score: MVII #HOUS_0,R1 ; 5ACB \ ADD CURPLR, R1 ; 5ACD |- Get number of houses MVI@ R1, R0 ; 5ACF / MVII #$01F4, R1 ; 5AD0 \_ Houses * 500 JSR R5, X_MPY ; 5AD2 / MOVR R2, R1 ; 5AD5 \ MVI G_0324, R2 ; 5AD6 |- (Houses * 500) / (pop / 100) JSR R5, X_DIVR ; 5AD8 / rounded MOVR R0, R1 ; 5ADB \ MVII #$0003, R2 ; 5ADC |- ((Houses*500)/(pop/100)) / 3 JSR R5, X_DIVR ; 5ADE / rounded CMPI #$001E, R0 ; 5AE1 \ BLE L_5AE7 ; 5AE3 |- Clamp housing score at 30 MVII #$001E, R0 ; 5AE5 / So what happens next? L_5AE7: MVO R0, H_SCOR ; 5AE7 Remember housing score That looks pretty innocuous, until you realize H_SCOR is in 8-bit memory. That negative score now looks like a large positive 8-bit value. This pattern repeats for the other subscores. MVII #RGLD_0,R4 ; 5AE9 \ ADD CURPLR, R4 ; 5AEB |- Get gold earned this round MVI@ R4, R0 ; 5AED / This is total GDP for round MVII #$0064, R1 ; 5AEE \_ Multiply by 100 JSR R5, X_MPY ; 5AF0 / MOVR R2, R1 ; 5AF3 \ Compute per capita GDP as MVI G_0324, R2 ; 5AF4 |- (gold * 100) / (pop / 100) JSR R5, X_DIVR ; 5AF6 / rounded MOVR R0, R1 ; 5AF9 \ Per capita GDP score is MVII #$000C, R2 ; 5AFA |- ((gold*100) / (pop/100)) / 12 JSR R5, X_DIVR ; 5AFC / rounded CMPI #$001E, R0 ; 5AFF \ BLE L_5B05 ; 5B01 |- Clamp GDP score at 30 MVII #$001E, R0 ; 5B03 / L_5B05: MVO R0, G_SCOR ; 5B05 Remember per capita GDP score G_SCOR is also in 8-bit memory. This will also look like a large positive 8-bit value. ;; Compute food-supply score ;; Note: This can overflow if (crops + fishing boats) > 65 MVII #F_BO_0,R1 ; 5B07 \ ADD CURPLR, R1 ; 5B09 |- Get number of fishing boats MVI@ R1, R0 ; 5B0B / MVII #CROP_0,R2 ; 5B0C \ ADD CURPLR, R2 ; 5B0E |_ Add number of crops MVI@ R2, R1 ; 5B10 | ADDR R0, R1 ; 5B11 / MVII #$01F4, R0 ; 5B12 \_ (boats + crops) * 500 JSR R5, X_MPY ; 5B14 / > Can go "negative" if more ; > than 65 fishing boats + crops MOVR R2, R1 ; 5B17 \ MVI G_0324, R2 ; 5B18 |- (boat+crops)*500 / (pop/100) JSR R5, X_DIVR ; 5B1A / MOVR R0, R1 ; 5B1D \ Food supply score: MVII #$0003, R2 ; 5B1E |- ((boat+crops)*500/(pop/100))/3 JSR R5, X_DIVR ; 5B20 / CMPI #$001E, R0 ; 5B23 \ BLE L_5B29 ; 5B25 |- Clamp at 30 MVII #$001E, R0 ; 5B27 / L_5B29: MVO R0, F_SCOR ; 5B29 Remember food score Ditto F_SCOR. Finally, they all get added together with the general welfare score as 16-bit arithmetic, and clamped to 100. Because the negative values above got whitewashed into looking like positive values, this sum is way larger than 100 but positive: CLRR R1 ; 5B2B \ Base round score: ADD H_SCOR, R1 ; 5B2C |_ housing + GDP + food ADD G_SCOR, R1 ; 5B2E | ADD F_SCOR, R1 ; 5B30 / ;; General welfare scoring: MVII #SCHL_0,R2 ; 5B32 \ ADD CURPLR, R2 ; 5B34 |_ Add 1 point for every school MVI@ R2, R0 ; 5B36 | ADDR R0, R1 ; 5B37 / MVII #HOSP_0,R2 ; 5B38 \ ADD CURPLR, R2 ; 5B3A |_ Add 1 point for every hospital MVI@ R2, R0 ; 5B3C | ADDR R0, R1 ; 5B3D / CMPI #$0064, R1 ; 5B3E \ BLE L_5B44 ; 5B40 |- Clamp round score at 100 MVII #$0064, R1 ; 5B42 / Mystery explained? 2 Quote Link to comment Share on other sites More sharing options...
BiffMan Posted March 4, 2020 Share Posted March 4, 2020 8 hours ago, intvnut said: Mystery explained? Beautifully, thank you!! That was awesome stuff and great to see how something so simple as passing a value from 16-bit to 8-bit memory can result in amusing secondary outcomes. Also really appreciate the confirmation on the -99 to 99 rounding explaining the score bomb-out. BITD, we used to think it was a matter of time before scores went to 100, but I much prefer a math-based explanation! ? The other fun bit this explains is why when you do go negative on population, you start seeing a slight increase in population (say from -110 to -106) each round if you are school-heavy. Since the big factor there is the negative fertility, not the mortality rate, and it's adding the fertility, you'd expect a negative pop plus a negative fertility, not a negative pop minus a positive mortality. I have to say, all your digging into the code makes me all the more impressed with Don Daglow's accomplishments. For the era, that's an incredible number of under-the-hood calculations going on to produce a very organic result. While it's fun to find the edges of the envelope where the calculations and 8-bit memory cause issues, that takes nothing away from how overall solid the game is! 1 Quote Link to comment Share on other sites More sharing options...
intvnut Posted March 4, 2020 Author Share Posted March 4, 2020 1 hour ago, BiffMan said: Also really appreciate the confirmation on the -99 to 99 rounding explaining the score bomb-out. I forgot one other detail: The divide routine returns 0 if you divide by 0. Since the code divides population by 100, rounding toward 0, this gives you a 0 divisor for a bunch of the calculations. Dividing by 0 population zeroes out those scores. That fleshes out the rest of the explanation. 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.