├── .gitignore ├── LICENSE ├── README.md ├── asset ├── arrowheads-processed.psd ├── arrowheads-scan.psd ├── object-pool-heap-fragment.psd ├── process.txt ├── style.scss ├── subclass-sandbox-coupling.psd ├── template.html └── template.xml ├── book ├── acknowledgements.markdown ├── architecture-performance-and-games.markdown ├── behavioral-patterns.markdown ├── bytecode.markdown ├── command.markdown ├── component.markdown ├── data-locality.markdown ├── decoupling-patterns.markdown ├── design-patterns-revisited.markdown ├── dirty-flag.markdown ├── double-buffer.markdown ├── event-queue.markdown ├── flyweight.markdown ├── game-loop.markdown ├── introduction.markdown ├── object-pool.markdown ├── observer.markdown ├── optimization-patterns.markdown ├── prototype.markdown ├── sequencing-patterns.markdown ├── service-locator.markdown ├── singleton.markdown ├── spatial-partition.markdown ├── state.markdown ├── subclass-sandbox.markdown ├── type-object.markdown └── update-method.markdown ├── build ├── code ├── cpp │ ├── bytecode.h │ ├── command.h │ ├── common.h │ ├── component.h │ ├── cpp.xcodeproj │ │ └── project.pbxproj │ ├── data-locality.h │ ├── dirty-flag.h │ ├── double-buffer.h │ ├── event-queue.h │ ├── expect.h │ ├── flyweight.h │ ├── game-loop.h │ ├── hello-world.h │ ├── introduction.h │ ├── main.cpp │ ├── object-pool.h │ ├── observer.h │ ├── prototype.h │ ├── service-locator.h │ ├── singleton.h │ ├── spatial-partition.h │ ├── state.h │ ├── subclass-sandbox.h │ ├── type-object.h │ └── update-method.h ├── dart │ └── spatial-partition │ │ └── pigeonhole_comparisons.dart ├── flyweight │ └── tiles │ │ ├── tiles.xcodeproj │ │ └── project.pbxproj │ │ └── tiles │ │ ├── main.cpp │ │ └── tiles.1 └── structure-of-arrays │ ├── activate │ ├── activate.xcodeproj │ │ └── project.pbxproj │ └── main.cpp │ ├── combined │ ├── combined.xcodeproj │ │ └── project.pbxproj │ └── main.cpp │ ├── component_update │ ├── component_update.xcodeproj │ │ └── project.pbxproj │ └── main.cpp │ ├── is_active │ ├── is_active.xcodeproj │ │ └── project.pbxproj │ └── main.cpp │ ├── random_array_access │ ├── main.cpp │ └── random_array_access.xcodeproj │ │ └── project.pbxproj │ └── shared │ └── utils.h ├── draft ├── first draft │ ├── game-loop.markdown │ ├── introduction.markdown │ ├── object-pool.markdown │ ├── service-locator.markdown │ ├── singleton.markdown │ ├── spatial-partition.markdown │ └── update-method.markdown └── outline │ ├── context-parameter.markdown │ ├── double-buffer.markdown │ ├── game-loop.markdown │ ├── hello-world.markdown │ ├── introduction.markdown │ ├── service.markdown │ ├── spatial-partition.markdown │ ├── subclass-sandbox.markdown │ ├── type-object.markdown │ └── update-method.markdown ├── html ├── acknowledgements.html ├── architecture-performance-and-games.html ├── behavioral-patterns.html ├── bytecode.html ├── command.html ├── component.html ├── data-locality.html ├── decoupling-patterns.html ├── design-patterns-revisited.html ├── dirty-flag.html ├── double-buffer.html ├── event-queue.html ├── flyweight.html ├── game-loop.html ├── images │ ├── Thumbs.db │ ├── architecture-cycle.png │ ├── arrow-inherits.png │ ├── arrow-references.png │ ├── background.png │ ├── bytecode-ast.png │ ├── bytecode-code.png │ ├── bytecode-literal.png │ ├── bytecode-numbers.png │ ├── bytecode-stack-1.png │ ├── bytecode-stack-2.png │ ├── bytecode-stack-3.png │ ├── bytecode-ui.png │ ├── command-buttons-one.png │ ├── command-buttons-two.png │ ├── command-stream.png │ ├── command-undo.png │ ├── component-uml.png │ ├── data-locality-cache-line.png │ ├── data-locality-chart.png │ ├── data-locality-component-arrays.png │ ├── data-locality-pointer-chasing.png │ ├── data-locality-things.png │ ├── dirty-flag-multiply.png │ ├── dirty-flag-pirate.png │ ├── dirty-flag-title-bar.png │ ├── dirty-flag-update-bad.png │ ├── dirty-flag-update-good.png │ ├── dogshot.jpg │ ├── double-buffer-face.png │ ├── double-buffer-slaps-1.png │ ├── double-buffer-slaps-2.png │ ├── double-buffer-tearing.png │ ├── event-queue-central.png │ ├── event-queue-crawl.png │ ├── event-queue-loop.png │ ├── event-queue-queue.png │ ├── event-queue-ring.png │ ├── flyweight-tiles.png │ ├── flyweight-tree-model.png │ ├── flyweight-trees.png │ ├── game-loop-fixed.png │ ├── game-loop-simple.png │ ├── game-loop-timeline-close.png │ ├── game-loop-timeline.png │ ├── object-pool-heap-fragment.png │ ├── observer-linked.png │ ├── observer-list.png │ ├── observer-nodes.png │ ├── observer-weasel-wielder.png │ ├── prototype-class.png │ ├── prototype-delegate.png │ ├── prototype-hierarchies.png │ ├── prototype-object.png │ ├── prototype-spawner.png │ ├── prototype-weapon.png │ ├── spatial-partition-adjacent.png │ ├── spatial-partition-battle-line.png │ ├── spatial-partition-grid.png │ ├── spatial-partition-linked-list.png │ ├── spatial-partition-neighbors.png │ ├── spatial-partition-quadtree.png │ ├── state-flowchart.png │ ├── state-pushdown.png │ ├── type-object-breed.png │ ├── type-object-subclasses.png │ ├── update-method-remove.png │ └── update-method-uml.png ├── index.html ├── introduction.html ├── jquery-3.6.0.min.js ├── object-pool.html ├── observer.html ├── optimization-patterns.html ├── prototype.html ├── script.js ├── sequencing-patterns.html ├── service-locator.html ├── singleton.html ├── spatial-partition.html ├── state.html ├── style.css ├── style.css.map ├── subclass-sandbox.html ├── type-object.html └── update-method.html ├── note ├── bio.txt ├── description.txt ├── dimensions.txt ├── fonts.txt ├── log.txt ├── print workflow.txt ├── publishing.txt ├── references.txt ├── style.markdown └── todo.txt ├── script └── format.py └── watch /.gitignore: -------------------------------------------------------------------------------- 1 | code/cpp/build/ 2 | code/flyweight/tiles/Build/ 3 | .sass-cache/ 4 | xcuserdata/ 5 | *.xcuserstate 6 | project.xcworkspace/ 7 | 8 | xml/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Robert Nystrom 2 | 3 | ------------------------ Commentary ------------------------ 4 | 5 | The licensing story for this repository is a little complex. 6 | Here's my motivation: 7 | 8 | * I want you to get as much use out of the material here as 9 | possible. I wrote this book to help you, and I don't want 10 | you to be encumbered when it comes to making the most of 11 | it. That's also why I put it online for free. 12 | 13 | * I have been immensely fortunate to have a large number of 14 | people contribute to the book through bug reports, fixes, 15 | pull requests, and other suggestions. Putting on GitHub 16 | and treating it like an open source project made a 17 | dramatic improvement in the resulting quality of the 18 | book. 19 | 20 | I want to ensure people can fork the repo, send me fixes, 21 | etc. without violating the license or feeling weird. 22 | 23 | * When it comes to code, I'm completely comfortable with 24 | people redistributing, remixing, changing, whatever with 25 | it. I've been using the MIT license for open source stuff 26 | for decades. 27 | 28 | * When it comes to my prose and the visual design of the 29 | site, that feels a little more, I don't know, *me* than 30 | the code. The words are in my voice, and the look of the 31 | site is part of its and, by extension, my brand. 32 | 33 | I feel weird thinking about someone, say taking one of the 34 | chapters and making significant changes to it to fit their 35 | writing style while still having some of it read like it 36 | came from me. Likewise, I'd be sad to see another site 37 | online that looked exactly like mine because it reuses my 38 | stylesheets. 39 | 40 | * Because -- and this came as a total surprise to me -- 41 | there are foreign publishers interested in the 42 | translation rights to the book, I want to be careful to 43 | not be so permissive that it prevents me from signing 44 | typical contracts that give them exclusive translation 45 | rights to certain territories and languages. 46 | 47 | * If I allow the prose to be redistributed commercially, 48 | there is nothing preventing someone from slapping 49 | together a cheap print or ebook version of the book and 50 | putting it up for sale. I'm not too worried about my own 51 | sales being undercut, but I very much want to avoid 52 | readers finding themselves with a low quality book that 53 | they incorrectly think is from me. 54 | 55 | I worked very hard on this book, including type setting, 56 | layout, generating the ePub file, styling it, etc. I want 57 | you to get the best possible experience. 58 | 59 | All of this is way more complex than I'd like, especially 60 | since my brain isn't wired to care about intellectual 61 | property. I like thinking about making stuff, not thinking 62 | about the legal rights around the stuff I made. (If your 63 | brain is wired to think about legal stuff and you see that 64 | I'm doing something dumb, please do let me know.) 65 | 66 | The best solution I've been able to come up with is to use 67 | two licenses: 68 | 69 | ------------------------ License(s) ------------------------ 70 | 71 | Each file in this repository falls under one of two 72 | licenses. Files whose extension is ".html", ".scss", ".css", 73 | ".markdown", or ".txt" use this Creative Commons license: 74 | 75 | Attribution-NonCommercial-NoDerivatives 4.0 76 | International (CC BY-NC-ND 4.0) 77 | 78 | https://creativecommons.org/licenses/by-nc-nd/4.0/ 79 | 80 | All other files, including (but not limited to) ".py", "h", 81 | and ".cpp" use the MIT license: 82 | 83 | Permission is hereby granted, free of charge, to any 84 | person obtaining a copy of this software and associated 85 | documentation files (the "Software"), to deal in the 86 | Software without restriction, including without 87 | limitation the rights to use, copy, modify, merge, 88 | publish, distribute, sublicense, and/or sell copies of 89 | the Software, and to permit persons to whom the Software 90 | is furnished to do so, subject to the following 91 | conditions: 92 | 93 | The above copyright notice and this permission notice 94 | shall be included in all copies or substantial portions 95 | of the Software. 96 | 97 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 98 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 99 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 100 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 101 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 102 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 103 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 104 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 105 | DEALINGS IN THE SOFTWARE. 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Note: Now that the book is done, I'm not actively working on it.** 2 | 3 | There are only so many hours in the day, and I have other projects that need my 4 | love, including a new book. This means I'm not responding to issues and pull 5 | requests here. However, I do intend to circle back and do a second edition at 6 | *some* point, so please do continue to file bugs. 7 | 8 | Even though I'm not responding, I do deeply appreciate every issue filed. 9 | Thank you! 10 | 11 | – bob 12 | 13 | --- 14 | 15 | This is the source repo for the book [Game Programming Patterns][]. 16 | 17 | ## Building the Book 18 | 19 | The book is written in Markdown (in `book/`). A little Python script (`script/format.py`) converts that along with a SASS file (`asset/style.scss`) and HTML template (`asset/template.html`) to the final HTML (in `html/`). To run the format script locally, you'll need to have Python 2.7-ish, and install Python Markdown, Pygments, and SmartyPants: 20 | 21 | $ pip install markdown 22 | $ pip install pygments 23 | $ pip install smartypants 24 | 25 | You may need `sudo` for those. Once that's done, you can run: 26 | 27 | $ python script/format.py 28 | 29 | Make sure to run this from the root directory of the repo. That will regenerate all of the chapter and section intro HTML files. If you're editing stuff, the script can also be run in watch mode: 30 | 31 | $ python script/format.py --watch 32 | 33 | That will monitor the file system for changes to the markdown files, SASS file, or HTML template, and reprocess them as needed. 34 | 35 | [game programming patterns]: http://gameprogrammingpatterns.com/ 36 | -------------------------------------------------------------------------------- /asset/arrowheads-processed.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/asset/arrowheads-processed.psd -------------------------------------------------------------------------------- /asset/arrowheads-scan.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/asset/arrowheads-scan.psd -------------------------------------------------------------------------------- /asset/object-pool-heap-fragment.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/asset/object-pool-heap-fragment.psd -------------------------------------------------------------------------------- /asset/process.txt: -------------------------------------------------------------------------------- 1 | 1. Desaturate. 2 | 2. Adjust the bottom end of the levels a bit to get the darkest to just be black (but no other grays). 3 | 3. Unsharp mask 90% 3.5 px 7 threshold 4 | 4. Now adjust the levels again to make the pen lines mostly solid black. Bring up the mids a bit so the grid lines aren't too dark. 5 | 5. Rotate about 1° CCW. 6 | 6. Add the yellow overlay. 7 | 7. Scale and crop to 1040 wide. 8 | 8. Save as 8-bit PNG with diffusion dither and 32 colors. -------------------------------------------------------------------------------- /asset/subclass-sandbox-coupling.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/asset/subclass-sandbox-coupling.psd -------------------------------------------------------------------------------- /asset/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{title}} · Game Programming Patterns 7 | 8 | 10 | 11 | 12 | 13 | 22 | 23 | 24 | 25 | 26 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /book/acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | ^title Acknowledge­ments 2 | 3 | I've heard only other authors know what's involved in writing a book, but there 4 | is another tribe who know the precise weight of that burden -- those with the 5 | misfortune of being in a relationship with a writer. I wrote this in a space of 6 | time painstakingly carved from the dense rock of life for me by my wife Megan. 7 | Washing dishes and giving the kids baths may not be "writing", but without her 8 | doing those, this book wouldn't be here. 9 | 10 | I started this project while a programmer at Electronic Arts. I don't think the 11 | company knew quite what to make of it, and I'm grateful to Michael Malone, 12 | Olivier Nallet, and Richard Wifall for supporting it and providing detailed, 13 | insightful feedback on the first few chapters. 14 | 15 | About halfway through writing, I decided to forgo a traditional publisher. I 16 | knew that meant losing the guidance an editor brings, but I had email from 17 | dozens of readers telling me where they wanted the book to go. I'd lose 18 | proofreaders, but I had over 250 bug reports to help improve the prose. I'd give 19 | up the incentive of a writing schedule, but with readers patting my back when I 20 | finished each chapter, I had more than enough motivation. 21 | 22 | 28 | 29 | They call this "self publishing", but "crowd publishing" is closer to the mark. 30 | Writing can be lonely work, but I was never alone. Even when I put the book on a 31 | shelf for two years, the encouragement continued. Without the dozens of people 32 | who didn't let me forget that they were waiting for more chapters, I never would 33 | have picked it back up and finished. 34 | 35 | 42 | 43 | To everyone who emailed or commented, upvoted or favorited, tweeted or 44 | retweeted, anyone who reached out to me, or told a friend about the book, or 45 | sent me a bug report: my heart is filled with gratitude for you. Completing this 46 | book was one of my biggest goals in life, and you made it happen. 47 | 48 | Thank you! 49 | -------------------------------------------------------------------------------- /book/behavioral-patterns.markdown: -------------------------------------------------------------------------------- 1 | ^title Behavioral Patterns 2 | 3 | Once you've built your game's set and festooned it with actors and props, all 4 | that remains is to start the scene. For this, you need behavior -- the 5 | screenplay that tells each entity in your game what to do. 6 | 7 | Of course all code is "behavior", and all software is defining behavior, but 8 | what's different about games is often the *breadth* of it that you have to 9 | implement. While your word processor may have a long list of features, it pales 10 | in comparison with the number of inhabitants, items, and quests in your average 11 | role-playing game. 12 | 13 | The patterns in this chapter help to quickly define and refine a large quantity of 14 | maintainable behavior. [Type Objects](type-object.html) create 15 | categories of behavior without the rigidity of defining an actual class. A 16 | [Subclass Sandbox](subclass-sandbox.html) gives you a safe set of primitives 17 | you can use to define a variety of behaviors. The most advanced option is 18 | [Bytecode](bytecode.html), which moves behavior out of code entirely and into 19 | data. 20 | 21 | ## The Patterns 22 | 23 | * [Bytecode](bytecode.html) 24 | * [Subclass Sandbox](subclass-sandbox.html) 25 | * [Type Object](type-object.html) 26 | -------------------------------------------------------------------------------- /book/decoupling-patterns.markdown: -------------------------------------------------------------------------------- 1 | ^title Decoupling Patterns 2 | 3 | Once you get the hang of a programming language, writing code to do what you 4 | want is actually pretty easy. What's hard is writing code that's easy to adapt 5 | when your requirements *change*. Rarely do we have the luxury of a perfect 6 | feature set before we've fired up our editor. 7 | 8 | A powerful tool we have for making change easier is *decoupling*. When we say 9 | two pieces of code are "decoupled", we mean a change in one usually doesn't 10 | require a change in the other. When you change some feature in your game, the 11 | fewer places in code you have to touch, the easier it is. 12 | 13 | [Components](component.html) decouple different domains in your game from each 14 | other within a single entity that has aspects of all of them. [Event 15 | Queues](event-queue.html) decouple two objects communicating with each other, 16 | both statically and *in time*. [Service Locators](service-locator.html) let 17 | code access a facility without being bound to the code that provides it. 18 | 19 | ## The Patterns 20 | 21 | * [Component](component.html) 22 | * [Event Queue](event-queue.html) 23 | * [Service Locator](service-locator.html) 24 | -------------------------------------------------------------------------------- /book/design-patterns-revisited.markdown: -------------------------------------------------------------------------------- 1 | ^title Design Patterns Revisited 2 | 3 | *Design Patterns: Elements of Reusable Object-Oriented Software* is nearly 4 | twenty years old by my watch. Unless you're looking over my shoulder, there's a 5 | good chance *Design Patterns* will be old enough to drink by the time you read 6 | this. For an industry as quickly moving as software, that's practically ancient. 7 | The enduring popularity of the book says something about how timeless design 8 | is compared to many frameworks and methodologies. 9 | 10 | While I think *Design Patterns* is still relevant, we've learned a lot in the 11 | past couple of decades. In this section, we'll walk through a handful of the 12 | original patterns the Gang of Four documented. For each pattern, I hope to have 13 | something useful or interesting to say. 14 | 15 | I think some patterns are overused (Singleton), 16 | while others are underappreciated (Command). A couple 17 | are in here because I want to explore their relevance specifically to games (Flyweight and Observer). 19 | Finally, sometimes I just think it's fun to see how patterns are enmeshed in 20 | the larger field of programming (Prototype and State). 22 | 23 | ## The Patterns 24 | 25 | * [Command](command.html) 26 | * [Flyweight](flyweight.html) 27 | * [Observer](observer.html) 28 | * [Prototype](prototype.html) 29 | * [Singleton](singleton.html) 30 | * [State](state.html) 31 | -------------------------------------------------------------------------------- /book/optimization-patterns.markdown: -------------------------------------------------------------------------------- 1 | ^title Optimization Patterns 2 | 3 | While the rising tide of faster and faster hardware has lifted most software 4 | above worrying about performance, games are one of the few remaining exceptions. 5 | Players always want richer, more realistic and exciting experiences. Screens are 6 | crowded with games vying for a player's attention -- and cash! -- and the game 7 | that pushes the hardware the furthest often wins. 8 | 9 | Optimizing for performance is a deep art that touches all aspects of software. 10 | Low-level coders master the myriad idiosyncrasies of hardware architectures. 11 | Meanwhile, algorithms researchers compete to prove mathematically whose 12 | procedure is the most efficient. 13 | 14 | Here, I touch on a few mid-level patterns that are often used to speed up a 15 | game. [Data Locality](data-locality.html) introduces you to the modern 16 | computer's memory hierarchy and how you can use it to your advantage. The [Dirty 17 | Flag](dirty-flag.html) pattern helps you avoid unnecessary computation while 18 | [Object Pools](object-pool.html) help you avoid unnecessary allocation. [Spatial 19 | Partitioning](spatial-partition.html) speeds up the virtual world and its 20 | inhabitants' arrangement in space. 21 | 22 | ## The Patterns 23 | 24 | * [Data Locality](data-locality.html) 25 | * [Dirty Flag](dirty-flag.html) 26 | * [Object Pool](object-pool.html) 27 | * [Spatial Partition](spatial-partition.html) 28 | -------------------------------------------------------------------------------- /book/sequencing-patterns.markdown: -------------------------------------------------------------------------------- 1 | ^title Sequencing Patterns 2 | 3 | Videogames are exciting in large part because they take us somewhere else. For a 4 | few minutes (or, let's be honest with ourselves, much longer), we become 5 | inhabitants of a virtual world. Creating these worlds is one of the supreme 6 | delights of being a game programmer. 7 | 8 | One aspect that most of these game worlds feature is *time* -- the artificial 9 | world lives and breathes at its own cadence. As world builders, we must invent 10 | time and craft the gears that drive our game's great clock. 11 | 12 | The patterns in this section are tools for doing just that. A [Game 13 | Loop](game-loop.html) is the central axle that the clock spins on. Objects hear 14 | its ticking through [Update Methods](update-method.html). We can hide the 15 | computer's sequential nature behind a facade of snapshots of moments in time 16 | using [Double Buffering](double-buffer.html) so that the world appears to 17 | update simultaneously. 18 | 19 | ## The Patterns 20 | 21 | * [Double Buffer](double-buffer.html) 22 | * [Game Loop](game-loop.html) 23 | * [Update Method](update-method.html) 24 | -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python3 script/format.py $@ 3 | -------------------------------------------------------------------------------- /code/cpp/command.h: -------------------------------------------------------------------------------- 1 | // 2 | // command.h 3 | // cpp 4 | // 5 | // Created by Bob Nystrom on 10/7/13. 6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved. 7 | // 8 | 9 | #ifndef cpp_command_h 10 | #define cpp_command_h 11 | 12 | namespace CommandPattern 13 | { 14 | enum Button 15 | { 16 | BUTTON_UP, 17 | BUTTON_DOWN, 18 | BUTTON_LEFT, 19 | BUTTON_RIGHT, 20 | BUTTON_X, 21 | BUTTON_Y, 22 | BUTTON_A, 23 | BUTTON_B 24 | }; 25 | 26 | bool isPressed(Button button) { return false; } 27 | void jump() {} 28 | void fireGun() {} 29 | void swapWeapon() {} 30 | void lurchIneffectively() {} 31 | 32 | namespace BeforeCommand 33 | { 34 | class InputHandler 35 | { 36 | public: 37 | void handleInput(); 38 | }; 39 | 40 | //^handle-input 41 | void InputHandler::handleInput() 42 | { 43 | if (isPressed(BUTTON_X)) jump(); 44 | else if (isPressed(BUTTON_Y)) fireGun(); 45 | else if (isPressed(BUTTON_A)) swapWeapon(); 46 | else if (isPressed(BUTTON_B)) lurchIneffectively(); 47 | } 48 | //^handle-input 49 | } 50 | 51 | namespace InputHandlingCommand 52 | { 53 | //^command 54 | class Command 55 | { 56 | public: 57 | virtual ~Command() {} 58 | virtual void execute() = 0; 59 | }; 60 | //^command 61 | 62 | //^command-classes 63 | class JumpCommand : public Command 64 | { 65 | public: 66 | virtual void execute() { jump(); } 67 | }; 68 | 69 | class FireCommand : public Command 70 | { 71 | public: 72 | virtual void execute() { fireGun(); } 73 | }; 74 | 75 | // You get the idea... 76 | //^command-classes 77 | 78 | //^input-handler-class 79 | class InputHandler 80 | { 81 | public: 82 | void handleInput(); 83 | 84 | // Methods to bind commands... 85 | 86 | private: 87 | Command* buttonX_; 88 | Command* buttonY_; 89 | Command* buttonA_; 90 | Command* buttonB_; 91 | }; 92 | //^input-handler-class 93 | 94 | //^handle-input-commands 95 | void InputHandler::handleInput() 96 | { 97 | if (isPressed(BUTTON_X)) buttonX_->execute(); 98 | else if (isPressed(BUTTON_Y)) buttonY_->execute(); 99 | else if (isPressed(BUTTON_A)) buttonA_->execute(); 100 | else if (isPressed(BUTTON_B)) buttonB_->execute(); 101 | } 102 | //^handle-input-commands 103 | } 104 | 105 | namespace CommandedActors 106 | { 107 | class GameActor 108 | { 109 | public: 110 | void jump() {} 111 | }; 112 | 113 | //^actor-command 114 | class Command 115 | { 116 | public: 117 | virtual ~Command() {} 118 | virtual void execute(GameActor& actor) = 0; 119 | }; 120 | //^actor-command 121 | 122 | //^jump-actor 123 | class JumpCommand : public Command 124 | { 125 | public: 126 | virtual void execute(GameActor& actor) 127 | { 128 | actor.jump(); 129 | } 130 | }; 131 | //^jump-actor 132 | 133 | class InputHandler 134 | { 135 | public: 136 | Command* handleInput(); 137 | private: 138 | Command* buttonX_; 139 | Command* buttonY_; 140 | Command* buttonA_; 141 | Command* buttonB_; 142 | }; 143 | 144 | //^handle-input-return 145 | Command* InputHandler::handleInput() 146 | { 147 | if (isPressed(BUTTON_X)) return buttonX_; 148 | if (isPressed(BUTTON_Y)) return buttonY_; 149 | if (isPressed(BUTTON_A)) return buttonA_; 150 | if (isPressed(BUTTON_B)) return buttonB_; 151 | 152 | // Nothing pressed, so do nothing. 153 | return NULL; 154 | } 155 | //^handle-input-return 156 | 157 | void executeCommand() 158 | { 159 | InputHandler inputHandler; 160 | GameActor actor; 161 | //^call-actor-command 162 | Command* command = inputHandler.handleInput(); 163 | if (command) 164 | { 165 | command->execute(actor); 166 | } 167 | //^call-actor-command 168 | use(actor); 169 | } 170 | } 171 | 172 | namespace Undo 173 | { 174 | class Unit { 175 | public: 176 | int x() { return 0; } 177 | int y() { return 0; } 178 | 179 | void moveTo(int x, int y) {} 180 | }; 181 | 182 | namespace UndoBefore 183 | { 184 | class Command 185 | { 186 | public: 187 | virtual ~Command() {} 188 | virtual void execute() = 0; 189 | }; 190 | 191 | //^move-unit 192 | class MoveUnitCommand : public Command 193 | { 194 | public: 195 | MoveUnitCommand(Unit* unit, int x, int y) 196 | : unit_(unit), 197 | x_(x), 198 | y_(y) 199 | {} 200 | 201 | virtual void execute() 202 | { 203 | unit_->moveTo(x_, y_); 204 | } 205 | 206 | private: 207 | Unit* unit_; 208 | int x_, y_; 209 | }; 210 | //^move-unit 211 | 212 | Unit* getSelectedUnit() { return NULL; } 213 | 214 | //^get-move 215 | Command* handleInput() 216 | { 217 | Unit* unit = getSelectedUnit(); 218 | 219 | if (isPressed(BUTTON_UP)) { 220 | // Move the unit up one. 221 | int destY = unit->y() - 1; 222 | return new MoveUnitCommand(unit, unit->x(), destY); 223 | } 224 | 225 | if (isPressed(BUTTON_DOWN)) { 226 | // Move the unit down one. 227 | int destY = unit->y() + 1; 228 | return new MoveUnitCommand(unit, unit->x(), destY); 229 | } 230 | 231 | // Other moves... 232 | 233 | return NULL; 234 | } 235 | //^get-move 236 | } 237 | 238 | namespace UndoAfter 239 | { 240 | //^undo-command 241 | class Command 242 | { 243 | public: 244 | virtual ~Command() {} 245 | virtual void execute() = 0; 246 | virtual void undo() = 0; 247 | }; 248 | //^undo-command 249 | 250 | //^undo-move-unit 251 | class MoveUnitCommand : public Command 252 | { 253 | public: 254 | MoveUnitCommand(Unit* unit, int x, int y) 255 | : unit_(unit), 256 | xBefore_(0), 257 | yBefore_(0), 258 | x_(x), 259 | y_(y) 260 | {} 261 | 262 | virtual void execute() 263 | { 264 | // Remember the unit's position before the move 265 | // so we can restore it. 266 | xBefore_ = unit_->x(); 267 | yBefore_ = unit_->y(); 268 | 269 | unit_->moveTo(x_, y_); 270 | } 271 | 272 | virtual void undo() 273 | { 274 | unit_->moveTo(xBefore_, yBefore_); 275 | } 276 | 277 | private: 278 | Unit* unit_; 279 | int xBefore_, yBefore_; 280 | int x_, y_; 281 | }; 282 | //^undo-move-unit 283 | } 284 | } 285 | } 286 | 287 | #endif 288 | -------------------------------------------------------------------------------- /code/cpp/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Makes the argument appear to be used so that we don't get an unused 4 | // variable warning for it. Lets us leave that warning on to catch unintended 5 | // unused variables. 6 | template 7 | void use(const T& obj) { 8 | // Do nothing. 9 | } 10 | 11 | #define ASSERT(condition) \ 12 | if (!(condition)) \ 13 | { \ 14 | std::cout << "FAIL: " #condition << "\n" << __FILE__ \ 15 | << ":" << __LINE__ << std::endl; \ 16 | abort(); \ 17 | } 18 | 19 | void assert(bool condition) { 20 | if (!condition) { 21 | printf("WTF\n"); 22 | exit(1); 23 | } 24 | } -------------------------------------------------------------------------------- /code/cpp/dirty-flag.h: -------------------------------------------------------------------------------- 1 | // 2 | // dirty-flag.h 3 | // cpp 4 | // 5 | // Created by Bob Nystrom on 9/5/13. 6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved. 7 | // 8 | 9 | #ifndef cpp_dirty_flag_h 10 | #define cpp_dirty_flag_h 11 | 12 | namespace DirtyFlag 13 | { 14 | //^transform 15 | class Transform 16 | { 17 | public: 18 | static Transform origin(); 19 | 20 | Transform combine(Transform& other); 21 | }; 22 | //^transform 23 | 24 | Transform Transform::origin() 25 | { 26 | return Transform(); 27 | } 28 | 29 | Transform Transform::combine(Transform& other) 30 | { 31 | return other; 32 | } 33 | 34 | class Mesh; 35 | static const int MAX_CHILDREN = 16; 36 | 37 | namespace Basic 38 | { 39 | //^graph-node 40 | class GraphNode 41 | { 42 | public: 43 | GraphNode(Mesh* mesh) 44 | : mesh_(mesh), 45 | local_(Transform::origin()) 46 | {} 47 | 48 | private: 49 | Transform local_; 50 | Mesh* mesh_; 51 | 52 | GraphNode* children_[MAX_CHILDREN]; 53 | int numChildren_; 54 | }; 55 | //^graph-node 56 | 57 | void root() 58 | { 59 | //^scene-graph 60 | GraphNode* graph_ = new GraphNode(NULL); 61 | // Add children to root graph node... 62 | //^scene-graph 63 | use(graph_); 64 | } 65 | } 66 | 67 | //^render 68 | void renderMesh(Mesh* mesh, Transform transform); 69 | //^render 70 | 71 | void renderMesh(Mesh* mesh, Transform transform) 72 | { 73 | 74 | } 75 | 76 | namespace RenderOnTheFly 77 | { 78 | static const int MAX_CHILDREN = 16; 79 | 80 | class GraphNode 81 | { 82 | public: 83 | GraphNode(Mesh* mesh) 84 | : mesh_(mesh), 85 | local_(Transform::origin()) 86 | {} 87 | 88 | void render(Transform parentWorld); 89 | 90 | private: 91 | Transform local_; 92 | Mesh* mesh_; 93 | 94 | GraphNode* children_[MAX_CHILDREN]; 95 | int numChildren_; 96 | }; 97 | 98 | //^render-on-fly 99 | void GraphNode::render(Transform parentWorld) 100 | { 101 | Transform world = local_.combine(parentWorld); 102 | 103 | if (mesh_) renderMesh(mesh_, world); 104 | 105 | for (int i = 0; i < numChildren_; i++) 106 | { 107 | children_[i]->render(world); 108 | } 109 | } 110 | //^render-on-fly 111 | 112 | void root() 113 | { 114 | GraphNode* graph_ = new GraphNode(NULL); 115 | //^render-root 116 | graph_->render(Transform::origin()); 117 | //^render-root 118 | } 119 | } 120 | 121 | namespace Dirty 122 | { 123 | //^dirty-graph-node 124 | class GraphNode 125 | { 126 | public: 127 | GraphNode(Mesh* mesh) 128 | : mesh_(mesh), 129 | local_(Transform::origin()), 130 | dirty_(true) 131 | {} 132 | //^omit 133 | void setTransform(Transform local); 134 | void render(Transform parentWorld, bool dirty); 135 | //^omit 136 | 137 | // Other methods... 138 | 139 | private: 140 | Transform world_; 141 | bool dirty_; 142 | // Other fields... 143 | //^omit 144 | Transform local_; 145 | Mesh* mesh_; 146 | 147 | static const int MAX_CHILDREN = 16; 148 | GraphNode* children_[MAX_CHILDREN]; 149 | int numChildren_; 150 | //^omit 151 | }; 152 | //^dirty-graph-node 153 | 154 | //^set-transform 155 | void GraphNode::setTransform(Transform local) 156 | { 157 | local_ = local; 158 | dirty_ = true; 159 | } 160 | //^set-transform 161 | 162 | //^dirty-render 163 | void GraphNode::render(Transform parentWorld, bool dirty) 164 | { 165 | dirty |= dirty_; 166 | if (dirty) 167 | { 168 | world_ = local_.combine(parentWorld); 169 | dirty_ = false; 170 | } 171 | 172 | if (mesh_) renderMesh(mesh_, world_); 173 | 174 | for (int i = 0; i < numChildren_; i++) 175 | { 176 | children_[i]->render(world_, dirty); 177 | } 178 | } 179 | //^dirty-render 180 | } 181 | } 182 | 183 | #endif 184 | -------------------------------------------------------------------------------- /code/cpp/double-buffer.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define WHITE 0 4 | #define BLACK 1 5 | 6 | //^1 7 | class Framebuffer 8 | { 9 | public: 10 | Framebuffer() { clear(); } 11 | 12 | void clear() 13 | { 14 | for (int i = 0; i < WIDTH * HEIGHT; i++) 15 | { 16 | pixels_[i] = WHITE; 17 | } 18 | } 19 | 20 | void draw(int x, int y) 21 | { 22 | pixels_[(WIDTH * y) + x] = BLACK; 23 | } 24 | 25 | const char* getPixels() 26 | { 27 | return pixels_; 28 | } 29 | 30 | private: 31 | static const int WIDTH = 160; 32 | static const int HEIGHT = 120; 33 | 34 | char pixels_[WIDTH * HEIGHT]; 35 | }; 36 | //^1 37 | 38 | namespace Unbuffered 39 | { 40 | //^2 41 | class Scene 42 | { 43 | public: 44 | void draw() 45 | { 46 | buffer_.clear(); 47 | 48 | buffer_.draw(1, 1); 49 | buffer_.draw(4, 1); 50 | buffer_.draw(1, 3); 51 | buffer_.draw(2, 4); 52 | buffer_.draw(3, 4); 53 | buffer_.draw(4, 3); 54 | } 55 | 56 | Framebuffer& getBuffer() { return buffer_; } 57 | 58 | private: 59 | Framebuffer buffer_; 60 | }; 61 | //^2 62 | 63 | void InterruptMiddleOfDraw() 64 | { 65 | Framebuffer buffer_; 66 | 67 | //^3 68 | buffer_.draw(1, 1); 69 | buffer_.draw(4, 1); 70 | // <- Video driver reads pixels here! 71 | buffer_.draw(1, 3); 72 | buffer_.draw(2, 4); 73 | buffer_.draw(3, 4); 74 | buffer_.draw(4, 3); 75 | //^3 76 | } 77 | } 78 | 79 | namespace Buffered 80 | { 81 | //^4 82 | class Scene 83 | { 84 | public: 85 | Scene() 86 | : current_(&buffers_[0]), 87 | next_(&buffers_[1]) 88 | {} 89 | 90 | void draw() 91 | { 92 | next_->clear(); 93 | 94 | next_->draw(1, 1); 95 | // ... 96 | next_->draw(4, 3); 97 | 98 | swap(); 99 | } 100 | 101 | Framebuffer& getBuffer() { return *current_; } 102 | 103 | private: 104 | void swap() 105 | { 106 | // Just switch the pointers. 107 | Framebuffer* temp = current_; 108 | current_ = next_; 109 | next_ = temp; 110 | } 111 | 112 | Framebuffer buffers_[2]; 113 | Framebuffer* current_; 114 | Framebuffer* next_; 115 | }; 116 | //^4 117 | } 118 | 119 | namespace UnbufferedSlapstick 120 | { 121 | class Stage; 122 | 123 | //^5 124 | class Actor 125 | { 126 | public: 127 | Actor() : slapped_(false) {} 128 | 129 | virtual ~Actor() {} 130 | virtual void update() = 0; 131 | 132 | void reset() { slapped_ = false; } 133 | void slap() { slapped_ = true; } 134 | bool wasSlapped() { return slapped_; } 135 | 136 | private: 137 | bool slapped_; 138 | }; 139 | //^5 140 | 141 | //^6 142 | class Stage 143 | { 144 | public: 145 | void add(Actor* actor, int index) 146 | { 147 | actors_[index] = actor; 148 | } 149 | 150 | void update() 151 | { 152 | for (int i = 0; i < NUM_ACTORS; i++) 153 | { 154 | actors_[i]->update(); 155 | actors_[i]->reset(); 156 | } 157 | } 158 | 159 | private: 160 | static const int NUM_ACTORS = 3; 161 | 162 | Actor* actors_[NUM_ACTORS]; 163 | }; 164 | //^6 165 | 166 | //^7 167 | class Comedian : public Actor 168 | { 169 | public: 170 | //^omit 171 | Comedian() : name_("") {} 172 | Comedian(const char* name) : name_(name) {} 173 | //^omit 174 | void face(Actor* actor) { facing_ = actor; } 175 | 176 | virtual void update() 177 | { 178 | //^omit 179 | if (wasSlapped()) std::cout << name_ << " was slapped" << std::endl; 180 | //^omit 181 | if (wasSlapped()) facing_->slap(); 182 | } 183 | 184 | private: 185 | //^omit 186 | const char* name_; 187 | //^omit 188 | Actor* facing_; 189 | }; 190 | //^7 191 | 192 | void sample1() 193 | { 194 | //^8 195 | Stage stage; 196 | 197 | Comedian* harry = new Comedian(); 198 | Comedian* baldy = new Comedian(); 199 | Comedian* chump = new Comedian(); 200 | 201 | harry->face(baldy); 202 | baldy->face(chump); 203 | chump->face(harry); 204 | 205 | stage.add(harry, 0); 206 | stage.add(baldy, 1); 207 | stage.add(chump, 2); 208 | //^8 209 | 210 | //^9 211 | harry->slap(); 212 | 213 | stage.update(); 214 | //^9 215 | 216 | //^10 217 | stage.add(harry, 2); 218 | stage.add(baldy, 1); 219 | stage.add(chump, 0); 220 | //^10 221 | } 222 | 223 | void testComedy1(int a, int b, int c) 224 | { 225 | std::cout << std::endl << "test" << std::endl; 226 | 227 | Stage stage; 228 | 229 | Comedian* larry = new Comedian("larry"); 230 | Comedian* curly = new Comedian("curly"); 231 | Comedian* shemp = new Comedian("shemp"); 232 | 233 | larry->face(curly); 234 | curly->face(shemp); 235 | shemp->face(larry); 236 | 237 | stage.add(larry, a); 238 | stage.add(curly, b); 239 | stage.add(shemp, c); 240 | 241 | larry->slap(); 242 | for (int i = 0; i < 3; i++) 243 | { 244 | std::cout << "update" << std::endl; 245 | stage.update(); 246 | } 247 | } 248 | 249 | void testComedy() 250 | { 251 | testComedy1(0, 1, 2); 252 | testComedy1(2, 1, 0); 253 | } 254 | } 255 | 256 | namespace BufferedSlapstick 257 | { 258 | class Stage; 259 | 260 | //^11 261 | class Actor 262 | { 263 | public: 264 | Actor() : currentSlapped_(false) {} 265 | 266 | virtual ~Actor() {} 267 | virtual void update() = 0; 268 | 269 | void swap() 270 | { 271 | // Swap the buffer. 272 | currentSlapped_ = nextSlapped_; 273 | 274 | // Clear the new "next" buffer. 275 | nextSlapped_ = false; 276 | } 277 | 278 | void slap() { nextSlapped_ = true; } 279 | bool wasSlapped() { return currentSlapped_; } 280 | 281 | private: 282 | bool currentSlapped_; 283 | bool nextSlapped_; 284 | }; 285 | //^11 286 | 287 | class Stage 288 | { 289 | public: 290 | void add(Actor* actor, int index) { actors_[index] = actor; } 291 | void update(); 292 | private: 293 | static const int NUM_ACTORS = 3; 294 | 295 | Actor* actors_[NUM_ACTORS]; 296 | }; 297 | 298 | //^12 299 | void Stage::update() 300 | { 301 | for (int i = 0; i < NUM_ACTORS; i++) 302 | { 303 | actors_[i]->update(); 304 | } 305 | 306 | for (int i = 0; i < NUM_ACTORS; i++) 307 | { 308 | actors_[i]->swap(); 309 | } 310 | } 311 | //^12 312 | 313 | class Comedian : public Actor 314 | { 315 | public: 316 | //^omit 317 | Comedian() : name_("") {} 318 | Comedian(const char* name) : name_(name) {} 319 | //^omit 320 | void face(Actor* actor) { facing_ = actor; } 321 | 322 | virtual void update() 323 | { 324 | //^omit 325 | if (wasSlapped()) std::cout << name_ << " was slapped" << std::endl; 326 | //^omit 327 | if (wasSlapped()) facing_->slap(); 328 | } 329 | 330 | private: 331 | //^omit 332 | const char* name_; 333 | //^omit 334 | Actor* facing_; 335 | }; 336 | 337 | void sample1() 338 | { 339 | Stage stage; 340 | 341 | Comedian* harry = new Comedian(); 342 | Comedian* baldy = new Comedian(); 343 | Comedian* chump = new Comedian(); 344 | 345 | harry->face(baldy); 346 | baldy->face(chump); 347 | chump->face(harry); 348 | 349 | stage.add(harry, 0); 350 | stage.add(baldy, 1); 351 | stage.add(chump, 2); 352 | 353 | harry->slap(); 354 | 355 | stage.update(); 356 | } 357 | } 358 | 359 | namespace SwapOffset 360 | { 361 | //^13 362 | class Actor 363 | { 364 | public: 365 | static void init() { current_ = 0; } 366 | static void swap() { current_ = next(); } 367 | 368 | void slap() { slapped_[next()] = true; } 369 | bool wasSlapped() { return slapped_[current_]; } 370 | 371 | private: 372 | static int current_; 373 | static int next() { return 1 - current_; } 374 | 375 | bool slapped_[2]; 376 | }; 377 | //^13 378 | } 379 | -------------------------------------------------------------------------------- /code/cpp/expect.h: -------------------------------------------------------------------------------- 1 | // 2 | // expect.h 3 | // gpp 4 | // 5 | // Created by Bob Nystrom on 6/25/13. 6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved. 7 | // 8 | 9 | #ifndef gpp_expect_h 10 | #define gpp_expect_h 11 | 12 | #define EXPECT(condition) \ 13 | expect_(__FILE__, __LINE__, #condition, condition) 14 | 15 | static void expect_(const char * file, int line, 16 | const char * expression, 17 | bool condition) 18 | { 19 | using std::cout; 20 | using std::endl; 21 | 22 | if (condition) 23 | { 24 | cout << "PASS: " << expression << endl; 25 | } 26 | else 27 | { 28 | cout << "FAIL: " << expression << endl; 29 | cout << " " << file << ":" << line << endl; 30 | } 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /code/cpp/flyweight.h: -------------------------------------------------------------------------------- 1 | // 2 | // flyweight.h 3 | // cpp 4 | // 5 | // Created by Bob Nystrom on 9/21/13. 6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved. 7 | // 8 | 9 | #ifndef cpp_flyweight_h 10 | #define cpp_flyweight_h 11 | 12 | namespace Flyweight 13 | { 14 | class Mesh {}; 15 | class Skeleton {}; 16 | class Texture {}; 17 | class Pose {}; 18 | class Vector {}; 19 | class Color {}; 20 | 21 | static const int WIDTH = 1024; 22 | static const int HEIGHT = 1024; 23 | 24 | static Texture GRASS_TEXTURE; 25 | static Texture HILL_TEXTURE; 26 | static Texture RIVER_TEXTURE; 27 | 28 | int random(int max) { return 0; } 29 | 30 | namespace HeavyTree 31 | { 32 | //^heavy-tree 33 | class Tree 34 | { 35 | private: 36 | Mesh mesh_; 37 | Texture bark_; 38 | Texture leaves_; 39 | Vector position_; 40 | double height_; 41 | double thickness_; 42 | Color barkTint_; 43 | Color leafTint_; 44 | }; 45 | //^heavy-tree 46 | } 47 | 48 | namespace SplitTree 49 | { 50 | //^tree-model 51 | class TreeModel 52 | { 53 | private: 54 | Mesh mesh_; 55 | Texture bark_; 56 | Texture leaves_; 57 | }; 58 | //^tree-model 59 | 60 | //^split-tree 61 | class Tree 62 | { 63 | private: 64 | TreeModel* model_; 65 | 66 | Vector position_; 67 | double height_; 68 | double thickness_; 69 | Color barkTint_; 70 | Color leafTint_; 71 | }; 72 | //^split-tree 73 | } 74 | 75 | namespace TerrainEnum 76 | { 77 | //^terrain-enum 78 | enum Terrain 79 | { 80 | TERRAIN_GRASS, 81 | TERRAIN_HILL, 82 | TERRAIN_RIVER 83 | // Other terrains... 84 | }; 85 | //^terrain-enum 86 | 87 | //^enum-world 88 | class World 89 | { 90 | private: 91 | Terrain tiles_[WIDTH][HEIGHT]; 92 | //^omit 93 | int getMovementCost(int x, int y); 94 | bool isWater(int x, int y); 95 | //^omit 96 | }; 97 | //^enum-world 98 | 99 | //^enum-data 100 | int World::getMovementCost(int x, int y) 101 | { 102 | switch (tiles_[x][y]) 103 | { 104 | case TERRAIN_GRASS: return 1; 105 | case TERRAIN_HILL: return 3; 106 | case TERRAIN_RIVER: return 2; 107 | // Other terrains... 108 | } 109 | } 110 | 111 | bool World::isWater(int x, int y) 112 | { 113 | switch (tiles_[x][y]) 114 | { 115 | case TERRAIN_GRASS: return false; 116 | case TERRAIN_HILL: return false; 117 | case TERRAIN_RIVER: return true; 118 | // Other terrains... 119 | } 120 | } 121 | //^enum-data 122 | } 123 | 124 | namespace TerrainClass 125 | { 126 | //^terrain-class 127 | class Terrain 128 | { 129 | public: 130 | Terrain(int movementCost, 131 | bool isWater, 132 | Texture texture) 133 | : movementCost_(movementCost), 134 | isWater_(isWater), 135 | texture_(texture) 136 | {} 137 | 138 | int getMovementCost() const { return movementCost_; } 139 | bool isWater() const { return isWater_; } 140 | const Texture& getTexture() const { return texture_; } 141 | 142 | private: 143 | int movementCost_; 144 | bool isWater_; 145 | Texture texture_; 146 | }; 147 | //^terrain-class 148 | 149 | //^world-terrain-pointers 150 | class World 151 | { 152 | //^omit 153 | public: 154 | World() 155 | : grassTerrain_(1, false, GRASS_TEXTURE), 156 | hillTerrain_(3, false, HILL_TEXTURE), 157 | riverTerrain_(2, true, RIVER_TEXTURE) 158 | {} 159 | const Terrain& getTile(int x, int y) const; 160 | //^omit 161 | private: 162 | Terrain* tiles_[WIDTH][HEIGHT]; 163 | 164 | // Other stuff... 165 | //^omit 166 | Terrain grassTerrain_; 167 | Terrain hillTerrain_; 168 | Terrain riverTerrain_; 169 | void generateTerrain(); 170 | //^omit 171 | }; 172 | //^world-terrain-pointers 173 | 174 | //^generate 175 | void World::generateTerrain() 176 | { 177 | // Fill the ground with grass. 178 | for (int x = 0; x < WIDTH; x++) 179 | { 180 | for (int y = 0; y < HEIGHT; y++) 181 | { 182 | // Sprinkle some hills. 183 | if (random(10) == 0) 184 | { 185 | tiles_[x][y] = &hillTerrain_; 186 | } 187 | else 188 | { 189 | tiles_[x][y] = &grassTerrain_; 190 | } 191 | } 192 | } 193 | 194 | // Lay a river. 195 | int x = random(WIDTH); 196 | for (int y = 0; y < HEIGHT; y++) { 197 | tiles_[x][y] = &riverTerrain_; 198 | } 199 | } 200 | //^generate 201 | 202 | //^get-tile 203 | const Terrain& World::getTile(int x, int y) const 204 | { 205 | return *tiles_[x][y]; 206 | } 207 | //^get-tile 208 | 209 | void foo() 210 | { 211 | World world; 212 | 213 | //^use-get-tile 214 | int cost = world.getTile(2, 3).getMovementCost(); 215 | //^use-get-tile 216 | use(cost); 217 | } 218 | } 219 | 220 | namespace WorldTerrain 221 | { 222 | class Terrain 223 | { 224 | public: 225 | Terrain(int movementCost, 226 | bool isWater, 227 | Texture texture) 228 | : movementCost_(movementCost), 229 | isWater_(isWater), 230 | texture_(texture) 231 | {} 232 | 233 | int getMovementCost() const { return movementCost_; } 234 | bool isWater() const { return isWater_; } 235 | const Texture& getTexture() const { return texture_; } 236 | 237 | private: 238 | int movementCost_; 239 | bool isWater_; 240 | Texture texture_; 241 | }; 242 | 243 | //^world-terrain 244 | class World 245 | { 246 | public: 247 | World() 248 | : grassTerrain_(1, false, GRASS_TEXTURE), 249 | hillTerrain_(3, false, HILL_TEXTURE), 250 | riverTerrain_(2, true, RIVER_TEXTURE) 251 | {} 252 | 253 | private: 254 | Terrain grassTerrain_; 255 | Terrain hillTerrain_; 256 | Terrain riverTerrain_; 257 | 258 | // Other stuff... 259 | }; 260 | //^world-terrain 261 | } 262 | } 263 | 264 | #endif 265 | -------------------------------------------------------------------------------- /code/cpp/game-loop.h: -------------------------------------------------------------------------------- 1 | void processInput() {} 2 | void update() {} 3 | void update(double elapsed) {} 4 | void render() {} 5 | void render(double elapsed) {} 6 | 7 | double getCurrentTime() { return 0; } 8 | void sleep(double time) {} 9 | 10 | int FPS = 60; 11 | int MS_PER_FRAME = 1000 / FPS; 12 | int MS_PER_TICK = 1000 / FPS; 13 | 14 | namespace Repl 15 | { 16 | char* readCommand() { return NULL; } 17 | void handleCommand(char* command) {} 18 | 19 | void loop() 20 | { 21 | //^1 22 | while (true) 23 | { 24 | char* command = readCommand(); 25 | handleCommand(command); 26 | } 27 | //^1 28 | } 29 | } 30 | 31 | namespace EventLoop 32 | { 33 | class Event {}; 34 | Event* waitForEvent() { return NULL; } 35 | bool dispatchEvent(Event* event) { return false; } 36 | 37 | void loop() 38 | { 39 | //^2 40 | while (true) 41 | { 42 | Event* event = waitForEvent(); 43 | dispatchEvent(event); 44 | } 45 | //^2 46 | } 47 | } 48 | 49 | namespace FastAsPossible 50 | { 51 | void runGame() 52 | { 53 | //^3 54 | while (true) 55 | { 56 | processInput(); 57 | update(); 58 | render(); 59 | } 60 | //^3 61 | } 62 | } 63 | 64 | namespace FixedFramerate 65 | { 66 | void runGame() 67 | { 68 | //^4 69 | while (true) 70 | { 71 | double start = getCurrentTime(); 72 | processInput(); 73 | update(); 74 | render(); 75 | 76 | sleep(start + MS_PER_FRAME - getCurrentTime()); 77 | } 78 | //^4 79 | } 80 | } 81 | 82 | 83 | namespace FluidFramerate 84 | { 85 | void runGame() 86 | { 87 | //^5 88 | double lastTime = getCurrentTime(); 89 | while (true) 90 | { 91 | double current = getCurrentTime(); 92 | double elapsed = current - lastTime; 93 | processInput(); 94 | update(elapsed); 95 | render(); 96 | lastTime = current; 97 | } 98 | //^5 99 | } 100 | } 101 | 102 | namespace FixedUpdateFramerate 103 | { 104 | void runGame() 105 | { 106 | const double MS_PER_UPDATE = 8; 107 | 108 | //^6 109 | double previous = getCurrentTime(); 110 | double lag = 0.0; 111 | while (true) 112 | { 113 | double current = getCurrentTime(); 114 | double elapsed = current - previous; 115 | previous = current; 116 | lag += elapsed; 117 | 118 | processInput(); 119 | 120 | while (lag >= MS_PER_UPDATE) 121 | { 122 | update(); 123 | lag -= MS_PER_UPDATE; 124 | } 125 | 126 | render(); 127 | } 128 | //^6 129 | } 130 | } 131 | 132 | 133 | namespace Interpolate 134 | { 135 | void runGame() 136 | { 137 | const double MS_PER_UPDATE = 8; 138 | double lag = 0; 139 | 140 | //^7 141 | render(lag / MS_PER_UPDATE); 142 | //^7 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /code/cpp/hello-world.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class HelloWorld 4 | { 5 | public: 6 | //^1 7 | // 64 characters --------------------------------------------------------| 8 | static void Do() 9 | { 10 | //^2 11 | std::cout << "Hello world." << std::endl; 12 | //^2 13 | } 14 | //^1 15 | }; -------------------------------------------------------------------------------- /code/cpp/introduction.h: -------------------------------------------------------------------------------- 1 | // 2 | // introduction.h 3 | // cpp 4 | // 5 | // Created by Bob Nystrom on 8/19/13. 6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved. 7 | // 8 | 9 | #ifndef cpp_introduction_h 10 | #define cpp_introduction_h 11 | 12 | //^update 13 | bool update() 14 | { 15 | // Do work... 16 | return isDone(); 17 | } 18 | //^update 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /code/cpp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "hello-world.h" 4 | #include "component.h" 5 | #include "object-pool.h" 6 | #include "service-locator.h" 7 | #include "singleton.h" 8 | #include "double-buffer.h" 9 | #include "subclass-sandbox.h" 10 | #include "type-object.h" 11 | #include "game-loop.h" 12 | #include "spatial-partition.h" 13 | #include "state.h" 14 | #include "update-method.h" 15 | #include "dirty-flag.h" 16 | #include "flyweight.h" 17 | #include "command.h" 18 | #include "observer.h" 19 | #include "prototype.h" 20 | #include "data-locality.h" 21 | #include "event-queue.h" 22 | #include "bytecode.h" 23 | 24 | int main (int argc, char * const argv[]) 25 | { 26 | UnbufferedSlapstick::testComedy(); 27 | SpatialPartition::test(); 28 | ObserverPattern::test(); 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /code/cpp/object-pool.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Version1 4 | { 5 | class ParticlePool; 6 | 7 | //^1 8 | class Particle 9 | { 10 | public: 11 | Particle() 12 | : framesLeft_(0) 13 | {} 14 | 15 | void init(double x, double y, 16 | double xVel, double yVel, int lifetime) 17 | { 18 | x_ = x; y_ = y; 19 | xVel_ = xVel; yVel_ = yVel; 20 | framesLeft_ = lifetime; 21 | } 22 | 23 | void animate() 24 | { 25 | if (!inUse()) return; 26 | 27 | framesLeft_--; 28 | x_ += xVel_; 29 | y_ += yVel_; 30 | } 31 | 32 | bool inUse() const { return framesLeft_ > 0; } 33 | 34 | private: 35 | int framesLeft_; 36 | double x_, y_; 37 | double xVel_, yVel_; 38 | }; 39 | //^1 40 | 41 | //^2 42 | class ParticlePool 43 | { 44 | public: 45 | void create(double x, double y, 46 | double xVel, double yVel, int lifetime); 47 | 48 | void animate() 49 | { 50 | for (int i = 0; i < POOL_SIZE; i++) 51 | { 52 | particles_[i].animate(); 53 | } 54 | } 55 | 56 | private: 57 | static const int POOL_SIZE = 100; 58 | Particle particles_[POOL_SIZE]; 59 | }; 60 | //^2 61 | 62 | //^3 63 | void ParticlePool::create(double x, double y, 64 | double xVel, double yVel, 65 | int lifetime) 66 | { 67 | // Find an available particle. 68 | for (int i = 0; i < POOL_SIZE; i++) 69 | { 70 | if (!particles_[i].inUse()) 71 | { 72 | particles_[i].init(x, y, xVel, yVel, lifetime); 73 | //^omit 74 | 75 | std::cout << "created " << i << std::endl; 76 | //^omit 77 | return; 78 | } 79 | } 80 | } 81 | //^3 82 | }; 83 | 84 | namespace Temp1 85 | { 86 | //^4 87 | class Particle 88 | { 89 | public: 90 | //^omit 91 | void init(double x, double y, 92 | double xVel, double yVel, int lifetime) {} 93 | bool animate(); 94 | bool inUse() { return false; } 95 | double x_; 96 | double y_; 97 | double xVel_; 98 | double yVel_; 99 | //^omit 100 | // ... 101 | 102 | Particle* getNext() const { return state_.next; } 103 | void setNext(Particle* next) { state_.next = next; } 104 | 105 | private: 106 | int framesLeft_; 107 | 108 | union 109 | { 110 | // State when it's in use. 111 | struct 112 | { 113 | double x, y; 114 | double xVel, yVel; 115 | } live; 116 | 117 | // State when it's available. 118 | Particle* next; 119 | } state_; 120 | }; 121 | //^4 122 | 123 | //^particle-animate 124 | bool Particle::animate() 125 | { 126 | if (!inUse()) return false; 127 | 128 | framesLeft_--; 129 | x_ += xVel_; 130 | y_ += yVel_; 131 | 132 | return framesLeft_ == 0; 133 | } 134 | //^particle-animate 135 | 136 | //^5 137 | class ParticlePool 138 | { 139 | //^omit 140 | ParticlePool(); 141 | void create(double x, double y, 142 | double xVel, double yVel, int lifetime); 143 | void animate(); 144 | //^omit 145 | // ... 146 | private: 147 | //^omit 148 | static const int POOL_SIZE = 100; 149 | Particle particles_[POOL_SIZE]; 150 | //^omit 151 | Particle* firstAvailable_; 152 | }; 153 | //^5 154 | 155 | //^6 156 | ParticlePool::ParticlePool() 157 | { 158 | // The first one is available. 159 | firstAvailable_ = &particles_[0]; 160 | 161 | // Each particle points to the next. 162 | for (int i = 0; i < POOL_SIZE - 1; i++) 163 | { 164 | particles_[i].setNext(&particles_[i + 1]); 165 | } 166 | 167 | // The last one terminates the list. 168 | particles_[POOL_SIZE - 1].setNext(NULL); 169 | } 170 | //^6 171 | 172 | //^7 173 | void ParticlePool::create(double x, double y, 174 | double xVel, double yVel, 175 | int lifetime) 176 | { 177 | // Make sure the pool isn't full. 178 | assert(firstAvailable_ != NULL); 179 | 180 | // Remove it from the available list. 181 | Particle* newParticle = firstAvailable_; 182 | firstAvailable_ = newParticle->getNext(); 183 | 184 | newParticle->init(x, y, xVel, yVel, lifetime); 185 | } 186 | //^7 187 | 188 | //^8 189 | void ParticlePool::animate() 190 | { 191 | for (int i = 0; i < POOL_SIZE; i++) 192 | { 193 | if (particles_[i].animate()) 194 | { 195 | // Add this particle to the front of the list. 196 | particles_[i].setNext(firstAvailable_); 197 | firstAvailable_ = &particles_[i]; 198 | } 199 | } 200 | } 201 | //^8 202 | }; 203 | 204 | namespace Temp2 205 | { 206 | class ParticlePool; 207 | 208 | //^10 209 | class Particle 210 | { 211 | friend class ParticlePool; 212 | 213 | private: 214 | Particle() 215 | : inUse_(false) 216 | {} 217 | 218 | bool inUse_; 219 | }; 220 | 221 | class ParticlePool 222 | { 223 | Particle pool_[100]; 224 | }; 225 | //^10 226 | } 227 | 228 | namespace Temp3 229 | { 230 | //^11 231 | template 232 | class GenericPool 233 | { 234 | private: 235 | static const int POOL_SIZE = 100; 236 | 237 | TObject pool_[POOL_SIZE]; 238 | bool inUse_[POOL_SIZE]; 239 | }; 240 | //^11 241 | } 242 | 243 | namespace Temp4 244 | { 245 | //^12 246 | class Particle 247 | { 248 | // Multiple ways to initialize. 249 | void init(double x, double y); 250 | void init(double x, double y, double angle); 251 | void init(double x, double y, double xVel, double yVel); 252 | }; 253 | 254 | class ParticlePool 255 | { 256 | public: 257 | void create(double x, double y) 258 | { 259 | // Forward to Particle... 260 | } 261 | 262 | void create(double x, double y, double angle) 263 | { 264 | // Forward to Particle... 265 | } 266 | 267 | void create(double x, double y, double xVel, double yVel) 268 | { 269 | // Forward to Particle... 270 | } 271 | }; 272 | //^12 273 | } 274 | 275 | namespace Temp5 276 | { 277 | //^13 278 | class Particle 279 | { 280 | public: 281 | // Multiple ways to initialize. 282 | void init(double x, double y); 283 | void init(double x, double y, double angle); 284 | void init(double x, double y, double xVel, double yVel); 285 | }; 286 | 287 | class ParticlePool 288 | { 289 | public: 290 | Particle* create() 291 | { 292 | // Return reference to available particle... 293 | //^omit 294 | return &pool_[0]; 295 | //^omit 296 | } 297 | private: 298 | Particle pool_[100]; 299 | }; 300 | //^13 301 | 302 | class Test 303 | { 304 | static void run() 305 | { 306 | //^14 307 | ParticlePool pool; 308 | 309 | pool.create()->init(1, 2); 310 | pool.create()->init(1, 2, 0.3); 311 | pool.create()->init(1, 2, 3.3, 4.4); 312 | //^14 313 | 314 | //^15 315 | Particle* particle = pool.create(); 316 | if (particle != NULL) particle->init(1, 2); 317 | //^15 318 | } 319 | }; 320 | }; 321 | 322 | // 64 characters --------------------------------------------------------| 323 | void TestParticlePool() 324 | { 325 | std::cout << "Object pool" << std::endl; 326 | 327 | Version1::ParticlePool pool = Version1::ParticlePool(); 328 | 329 | pool.create(0, 0, 1, 1, 10); 330 | pool.create(1, 0, 1, 1, 10); 331 | pool.create(2, 0, 1, 1, 10); 332 | // pool.update(); 333 | } -------------------------------------------------------------------------------- /code/cpp/prototype.h: -------------------------------------------------------------------------------- 1 | #ifndef cpp_prototype_h 2 | #define cpp_prototype_h 3 | 4 | namespace PrototypePattern 5 | { 6 | namespace Classes 7 | { 8 | //^monster-classes 9 | class Monster 10 | { 11 | // Stuff... 12 | }; 13 | 14 | class Ghost : public Monster {}; 15 | class Demon : public Monster {}; 16 | class Sorcerer : public Monster {}; 17 | //^monster-classes 18 | 19 | //^spawner-classes 20 | class Spawner 21 | { 22 | public: 23 | virtual ~Spawner() {} 24 | virtual Monster* spawnMonster() = 0; 25 | }; 26 | 27 | class GhostSpawner : public Spawner 28 | { 29 | public: 30 | virtual Monster* spawnMonster() 31 | { 32 | return new Ghost(); 33 | } 34 | }; 35 | 36 | class DemonSpawner : public Spawner 37 | { 38 | public: 39 | virtual Monster* spawnMonster() 40 | { 41 | return new Demon(); 42 | } 43 | }; 44 | 45 | // You get the idea... 46 | //^spawner-classes 47 | } 48 | 49 | namespace Clone 50 | { 51 | //^virtual-clone 52 | class Monster 53 | { 54 | public: 55 | virtual ~Monster() {} 56 | virtual Monster* clone() = 0; 57 | 58 | // Other stuff... 59 | }; 60 | //^virtual-clone 61 | 62 | //^clone-ghost 63 | class Ghost : public Monster { 64 | public: 65 | Ghost(int health, int speed) 66 | : health_(health), 67 | speed_(speed) 68 | {} 69 | 70 | virtual Monster* clone() 71 | { 72 | return new Ghost(health_, speed_); 73 | } 74 | 75 | private: 76 | int health_; 77 | int speed_; 78 | }; 79 | //^clone-ghost 80 | 81 | 82 | //^spawner-clone 83 | class Spawner 84 | { 85 | public: 86 | Spawner(Monster* prototype) 87 | : prototype_(prototype) 88 | {} 89 | 90 | Monster* spawnMonster() 91 | { 92 | return prototype_->clone(); 93 | } 94 | 95 | private: 96 | Monster* prototype_; 97 | }; 98 | //^spawner-clone 99 | 100 | void test() 101 | { 102 | //^spawn-ghost-clone 103 | Monster* ghostPrototype = new Ghost(15, 3); 104 | Spawner* ghostSpawner = new Spawner(ghostPrototype); 105 | //^spawn-ghost-clone 106 | use(ghostSpawner); 107 | } 108 | } 109 | 110 | namespace Callbacks 111 | { 112 | class Monster 113 | { 114 | // Stuff... 115 | }; 116 | 117 | class Ghost : public Monster {}; 118 | 119 | //^callback 120 | Monster* spawnGhost() 121 | { 122 | return new Ghost(); 123 | } 124 | //^callback 125 | 126 | //^spawner-callback 127 | typedef Monster* (*SpawnCallback)(); 128 | 129 | class Spawner 130 | { 131 | public: 132 | Spawner(SpawnCallback spawn) 133 | : spawn_(spawn) 134 | {} 135 | 136 | Monster* spawnMonster() 137 | { 138 | return spawn_(); 139 | } 140 | 141 | private: 142 | SpawnCallback spawn_; 143 | }; 144 | //^spawner-callback 145 | 146 | void test() 147 | { 148 | //^spawn-ghost-callback 149 | Spawner* ghostSpawner = new Spawner(spawnGhost); 150 | //^spawn-ghost-callback 151 | use(ghostSpawner); 152 | } 153 | } 154 | 155 | namespace Templates 156 | { 157 | class Monster 158 | { 159 | // Stuff... 160 | }; 161 | 162 | class Ghost : public Monster {}; 163 | 164 | //^templates 165 | class Spawner 166 | { 167 | public: 168 | virtual ~Spawner() {} 169 | virtual Monster* spawnMonster() = 0; 170 | }; 171 | 172 | template 173 | class SpawnerFor : public Spawner 174 | { 175 | public: 176 | virtual Monster* spawnMonster() { return new T(); } 177 | }; 178 | //^templates 179 | 180 | void test() 181 | { 182 | //^use-templates 183 | Spawner* ghostSpawner = new SpawnerFor(); 184 | //^use-templates 185 | use(ghostSpawner); 186 | } 187 | } 188 | } 189 | 190 | #endif 191 | -------------------------------------------------------------------------------- /code/cpp/service-locator.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class AudioSystem 4 | { 5 | public: 6 | static void playSound(int id) {} 7 | static AudioSystem* instance() { return NULL; } 8 | }; 9 | 10 | void example() 11 | { 12 | int VERY_LOUD_BANG = 0; 13 | 14 | //^15 15 | // Use a static class? 16 | AudioSystem::playSound(VERY_LOUD_BANG); 17 | 18 | // Or maybe a singleton? 19 | AudioSystem::instance()->playSound(VERY_LOUD_BANG); 20 | //^15 21 | } 22 | 23 | //^9 24 | class Audio 25 | { 26 | public: 27 | virtual ~Audio() {} 28 | virtual void playSound(int soundID) = 0; 29 | virtual void stopSound(int soundID) = 0; 30 | virtual void stopAllSounds() = 0; 31 | }; 32 | //^9 33 | 34 | //^10 35 | class ConsoleAudio : public Audio 36 | { 37 | public: 38 | virtual void playSound(int soundID) 39 | { 40 | // Play sound using console audio api... 41 | } 42 | 43 | virtual void stopSound(int soundID) 44 | { 45 | // Stop sound using console audio api... 46 | } 47 | 48 | virtual void stopAllSounds() 49 | { 50 | // Stop all sounds using console audio api... 51 | } 52 | }; 53 | //^10 54 | 55 | //^12 56 | class LoggedAudio : public Audio 57 | { 58 | public: 59 | LoggedAudio(Audio &wrapped) 60 | : wrapped_(wrapped) 61 | {} 62 | 63 | virtual void playSound(int soundID) 64 | { 65 | log("play sound"); 66 | wrapped_.playSound(soundID); 67 | } 68 | 69 | virtual void stopSound(int soundID) 70 | { 71 | log("stop sound"); 72 | wrapped_.stopSound(soundID); 73 | } 74 | 75 | virtual void stopAllSounds() 76 | { 77 | log("stop all sounds"); 78 | wrapped_.stopAllSounds(); 79 | } 80 | 81 | private: 82 | void log(const char* message) 83 | { 84 | // Code to log message... 85 | } 86 | 87 | Audio &wrapped_; 88 | }; 89 | //^12 90 | 91 | // design decisions / di 92 | namespace Version1 93 | { 94 | //^1 95 | class Locator 96 | { 97 | public: 98 | static Audio* getAudio() { return service_; } 99 | 100 | static void provide(Audio* service) 101 | { 102 | service_ = service; 103 | } 104 | 105 | private: 106 | static Audio* service_; 107 | }; 108 | //^1 109 | 110 | Audio *Locator::service_; 111 | 112 | void initGame() 113 | { 114 | //^11 115 | ConsoleAudio *audio = new ConsoleAudio(); 116 | Locator::provide(audio); 117 | //^11 118 | } 119 | 120 | void someGameCode() 121 | { 122 | int VERY_LOUD_BANG = 0; 123 | //^5 124 | Audio *audio = Locator::getAudio(); 125 | audio->playSound(VERY_LOUD_BANG); 126 | //^5 127 | } 128 | } 129 | 130 | // design decisions / compile time 131 | namespace Version2 132 | { 133 | class DebugAudio: public Audio 134 | { 135 | public: 136 | virtual void playSound(int soundID) { /* Do nothing. */ } 137 | virtual void stopSound(int soundID) { /* Do nothing. */ } 138 | virtual void stopAllSounds() { /* Do nothing. */ } 139 | }; 140 | class ReleaseAudio: public DebugAudio {}; 141 | 142 | //^2 143 | class Locator 144 | { 145 | public: 146 | static Audio& getAudio() { return service_; } 147 | 148 | private: 149 | #if DEBUG 150 | static DebugAudio service_; 151 | #else 152 | static ReleaseAudio service_; 153 | #endif 154 | }; 155 | //^2 156 | } 157 | 158 | // design decisions / scope 159 | namespace Version3 160 | { 161 | //^3 162 | class Base 163 | { 164 | // Code to locate service and set service_... 165 | 166 | protected: 167 | // Derived classes can use service 168 | static Audio& getAudio() { return *service_; } 169 | 170 | private: 171 | static Audio* service_; 172 | }; 173 | //^3 174 | } 175 | 176 | namespace Version4 177 | { 178 | //^4 179 | class Locator 180 | { 181 | public: 182 | static Audio& getAudio() 183 | { 184 | Audio* service = NULL; 185 | 186 | // Code here to locate service... 187 | 188 | assert(service != NULL); 189 | return *service; 190 | } 191 | }; 192 | //^4 193 | } 194 | 195 | namespace Version5 196 | { 197 | //^7 198 | class NullAudio: public Audio 199 | { 200 | public: 201 | virtual void playSound(int soundID) { /* Do nothing. */ } 202 | virtual void stopSound(int soundID) { /* Do nothing. */ } 203 | virtual void stopAllSounds() { /* Do nothing. */ } 204 | }; 205 | //^7 206 | 207 | //^8 208 | class Locator 209 | { 210 | public: 211 | static void initialize() { service_ = &nullService_; } 212 | 213 | static Audio& getAudio() { return *service_; } 214 | 215 | static void provide(Audio* service) 216 | { 217 | if (service == NULL) 218 | { 219 | // Revert to null service. 220 | service_ = &nullService_; 221 | } 222 | else 223 | { 224 | service_ = service; 225 | } 226 | } 227 | 228 | private: 229 | static Audio* service_; 230 | static NullAudio nullService_; 231 | }; 232 | //^8 233 | 234 | Audio *Locator::service_ = NULL; 235 | NullAudio Locator::nullService_; 236 | 237 | //^13 238 | void enableAudioLogging() 239 | { 240 | // Decorate the existing service. 241 | Audio *service = new LoggedAudio(Locator::getAudio()); 242 | 243 | // Swap it in. 244 | Locator::provide(service); 245 | } 246 | //^13 247 | } -------------------------------------------------------------------------------- /code/cpp/singleton.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Singleton1 4 | { 5 | //^1 6 | class FileSystem 7 | { 8 | public: 9 | static FileSystem& instance() 10 | { 11 | // Lazy initialize. 12 | if (instance_ == NULL) instance_ = new FileSystem(); 13 | return *instance_; 14 | } 15 | 16 | private: 17 | FileSystem() {} 18 | 19 | static FileSystem* instance_; 20 | }; 21 | //^1 22 | 23 | FileSystem* FileSystem::instance_; 24 | 25 | void test() 26 | { 27 | if (&FileSystem::instance() == NULL) 28 | { 29 | std::cout << "singleton is null!" << std::endl; 30 | } 31 | else 32 | { 33 | std::cout << "singleton is ok" << std::endl; 34 | } 35 | } 36 | } 37 | 38 | namespace SingletonStatic 39 | { 40 | //^local-static 41 | class FileSystem 42 | { 43 | public: 44 | static FileSystem& instance() 45 | { 46 | static FileSystem *instance = new FileSystem(); 47 | return *instance; 48 | } 49 | 50 | private: 51 | FileSystem() {} 52 | }; 53 | //^local-static 54 | 55 | void test() 56 | { 57 | if (&FileSystem::instance() == NULL) 58 | { 59 | std::cout << "singleton is null!" << std::endl; 60 | } 61 | else 62 | { 63 | std::cout << "singleton is ok" << std::endl; 64 | } 65 | } 66 | } 67 | 68 | namespace Singleton2 69 | { 70 | //^2 71 | class FileSystem 72 | { 73 | public: 74 | virtual ~FileSystem() {} 75 | virtual char* readFile(char* path) = 0; 76 | virtual void writeFile(char* path, char* contents) = 0; 77 | }; 78 | //^2 79 | 80 | //^derived-file-systems 81 | class PS3FileSystem : public FileSystem 82 | { 83 | public: 84 | virtual char* readFile(char* path) 85 | { 86 | // Use Sony file IO API... 87 | //^omit 88 | return NULL; 89 | //^omit 90 | } 91 | 92 | virtual void writeFile(char* path, char* contents) 93 | { 94 | // Use sony file IO API... 95 | } 96 | }; 97 | 98 | class WiiFileSystem : public FileSystem 99 | { 100 | public: 101 | virtual char* readFile(char* path) 102 | { 103 | // Use Nintendo file IO API... 104 | //^omit 105 | return NULL; 106 | //^omit 107 | } 108 | 109 | virtual void writeFile(char* path, char* contents) 110 | { 111 | // Use Nintendo file IO API... 112 | } 113 | }; 114 | //^derived-file-systems 115 | } 116 | 117 | namespace Singleton3 118 | { 119 | #define PLAYSTATION3 1 120 | #define WII 2 121 | #define PLATFORM PLAYSTATION3 122 | 123 | class PS3FileSystem; 124 | class WiiFileSystem; 125 | 126 | //^3 127 | class FileSystem 128 | { 129 | public: 130 | static FileSystem& instance(); 131 | 132 | virtual ~FileSystem() {} 133 | virtual char* readFile(char* path) = 0; 134 | virtual void writeFile(char* path, char* contents) = 0; 135 | 136 | protected: 137 | FileSystem() {} 138 | }; 139 | //^3 140 | 141 | class PS3FileSystem : public FileSystem 142 | { 143 | virtual char* readFile(char* path) 144 | { 145 | return NULL; 146 | } 147 | 148 | virtual void writeFile(char* path, char* contents) {} 149 | }; 150 | 151 | class WiiFileSystem : public FileSystem 152 | { 153 | virtual char* readFile(char* path) 154 | { 155 | return NULL; 156 | } 157 | 158 | virtual void writeFile(char* path, char* contents) {} 159 | }; 160 | 161 | //^4 162 | FileSystem& FileSystem::instance() 163 | { 164 | #if PLATFORM == PLAYSTATION3 165 | static FileSystem *instance = new PS3FileSystem(); 166 | #elif PLATFORM == WII 167 | static FileSystem *instance = new WiiFileSystem(); 168 | #endif 169 | 170 | return *instance; 171 | } 172 | //^4 173 | } 174 | 175 | namespace Singleton4 176 | { 177 | //^5 178 | class FileSystem 179 | { 180 | public: 181 | static FileSystem& instance() { return instance_; } 182 | 183 | private: 184 | FileSystem() {} 185 | 186 | static FileSystem instance_; 187 | }; 188 | //^5 189 | 190 | FileSystem FileSystem::instance_ = FileSystem(); 191 | } 192 | 193 | namespace Singleton5 194 | { 195 | //^6 196 | class FileSystem 197 | { 198 | public: 199 | FileSystem() 200 | { 201 | assert(!instantiated_); 202 | instantiated_ = true; 203 | } 204 | 205 | ~FileSystem() { instantiated_ = false; } 206 | 207 | private: 208 | static bool instantiated_; 209 | }; 210 | 211 | bool FileSystem::instantiated_ = false; 212 | //^6 213 | } 214 | 215 | namespace Singleton7 216 | { 217 | #define SCREEN_WIDTH 100 218 | #define SCREEN_HEIGHT 100 219 | 220 | //^8 221 | class Bullet 222 | { 223 | public: 224 | int getX() const { return x_; } 225 | int getY() const { return y_; } 226 | 227 | void setX(int x) { x_ = x; } 228 | void setY(int y) { y_ = y; } 229 | 230 | private: 231 | int x_, y_; 232 | }; 233 | 234 | class BulletManager 235 | { 236 | public: 237 | Bullet* create(int x, int y) 238 | { 239 | Bullet* bullet = new Bullet(); 240 | bullet->setX(x); 241 | bullet->setY(y); 242 | 243 | return bullet; 244 | } 245 | 246 | bool isOnScreen(Bullet& bullet) 247 | { 248 | return bullet.getX() >= 0 && 249 | bullet.getX() < SCREEN_WIDTH && 250 | bullet.getY() >= 0 && 251 | bullet.getY() < SCREEN_HEIGHT; 252 | } 253 | 254 | void move(Bullet& bullet) 255 | { 256 | bullet.setX(bullet.getX() + 5); 257 | } 258 | }; 259 | //^8 260 | } 261 | 262 | namespace Singleton8 263 | { 264 | //^9 265 | class Bullet 266 | { 267 | public: 268 | Bullet(int x, int y) : x_(x), y_(y) {} 269 | 270 | bool isOnScreen() 271 | { 272 | return x_ >= 0 && x_ < SCREEN_WIDTH && 273 | y_ >= 0 && y_ < SCREEN_HEIGHT; 274 | } 275 | 276 | void move() { x_ += 5; } 277 | 278 | private: 279 | int x_, y_; 280 | }; 281 | //^9 282 | } 283 | 284 | namespace Singleton9 285 | { 286 | class Log 287 | { 288 | public: 289 | virtual ~Log() {} 290 | virtual void write(const char* text) = 0; 291 | }; 292 | 293 | //^10 294 | class GameObject 295 | { 296 | protected: 297 | Log& getLog() { return log_; } 298 | 299 | private: 300 | static Log& log_; 301 | }; 302 | 303 | class Enemy : public GameObject 304 | { 305 | void doSomething() 306 | { 307 | getLog().write("I can log!"); 308 | } 309 | }; 310 | //^10 311 | } 312 | 313 | namespace Singleton10 314 | { 315 | class Log {}; 316 | class FileSystem {}; 317 | class AudioPlayer 318 | { 319 | public: 320 | virtual void play(int id) = 0; 321 | }; 322 | 323 | //^11 324 | class Game 325 | { 326 | public: 327 | static Game& instance() { return instance_; } 328 | 329 | // Functions to set log_, et. al. ... 330 | 331 | Log& getLog() { return *log_; } 332 | FileSystem& getFileSystem() { return *fileSystem_; } 333 | AudioPlayer& getAudioPlayer() { return *audioPlayer_; } 334 | 335 | private: 336 | static Game instance_; 337 | 338 | Log *log_; 339 | FileSystem *fileSystem_; 340 | AudioPlayer *audioPlayer_; 341 | }; 342 | //^11 343 | 344 | Game Game::instance_ = Game(); 345 | 346 | void foo() 347 | { 348 | int VERY_LOUD_BANG = 0; 349 | //^12 350 | Game::instance().getAudioPlayer().play(VERY_LOUD_BANG); 351 | //^12 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /code/cpp/subclass-sandbox.h: -------------------------------------------------------------------------------- 1 | typedef int SoundId; 2 | typedef int ParticleType; 3 | 4 | const SoundId SOUND_SPROING = 1; 5 | const SoundId SOUND_SWOOP = 1; 6 | const SoundId SOUND_DIVE = 1; 7 | const ParticleType PARTICLE_DUST = 1; 8 | const ParticleType PARTICLE_SPARKLES = 1; 9 | 10 | namespace SimpleExample 11 | { 12 | //^1 13 | class Superpower 14 | { 15 | public: 16 | virtual ~Superpower() {} 17 | 18 | protected: 19 | virtual void activate() = 0; 20 | 21 | void move(double x, double y, double z) 22 | { 23 | // Code here... 24 | } 25 | 26 | void playSound(SoundId sound, double volume) 27 | { 28 | // Code here... 29 | } 30 | 31 | void spawnParticles(ParticleType type, int count) 32 | { 33 | // Code here... 34 | } 35 | }; 36 | //^1 37 | 38 | //^2 39 | class SkyLaunch : public Superpower 40 | { 41 | protected: 42 | virtual void activate() 43 | { 44 | // Spring into the air. 45 | playSound(SOUND_SPROING, 1.0f); 46 | spawnParticles(PARTICLE_DUST, 10); 47 | move(0, 0, 20); 48 | } 49 | }; 50 | //^2 51 | } 52 | 53 | namespace Elaborated 54 | { 55 | //^3 56 | class Superpower 57 | { 58 | protected: 59 | //^omit 60 | virtual void activate() = 0; 61 | void move(double x, double y, double z) {} 62 | void playSound(SoundId sound, double volume) {} 63 | void spawnParticles(ParticleType type, int count) {} 64 | //^omit 65 | double getHeroX() 66 | { 67 | // Code here... 68 | //^omit 69 | return 0; 70 | //^omit 71 | } 72 | 73 | double getHeroY() 74 | { 75 | // Code here... 76 | //^omit 77 | return 0; 78 | //^omit 79 | } 80 | 81 | double getHeroZ() 82 | { 83 | // Code here... 84 | //^omit 85 | return 0; 86 | //^omit 87 | } 88 | 89 | // Existing stuff... 90 | }; 91 | //^3 92 | 93 | //^4 94 | class SkyLaunch : public Superpower 95 | { 96 | protected: 97 | virtual void activate() 98 | { 99 | if (getHeroZ() == 0) 100 | { 101 | // On the ground, so spring into the air. 102 | playSound(SOUND_SPROING, 1.0f); 103 | spawnParticles(PARTICLE_DUST, 10); 104 | move(0, 0, 20); 105 | } 106 | else if (getHeroZ() < 10.0f) 107 | { 108 | // Near the ground, so do a double jump. 109 | playSound(SOUND_SWOOP, 1.0f); 110 | move(0, 0, getHeroZ() + 20); 111 | } 112 | else 113 | { 114 | // Way up in the air, so do a dive attack. 115 | playSound(SOUND_DIVE, 0.7f); 116 | spawnParticles(PARTICLE_SPARKLES, 1); 117 | move(0, 0, -getHeroZ()); 118 | } 119 | } 120 | }; 121 | //^4 122 | } 123 | 124 | namespace Forwarding 125 | { 126 | struct SoundEngine 127 | { 128 | void play(SoundId sound, double volume) {} 129 | }; 130 | 131 | SoundEngine soundEngine_; 132 | 133 | //^5 134 | void playSound(SoundId sound, double volume) 135 | { 136 | soundEngine_.play(sound, volume); 137 | } 138 | //^5 139 | } 140 | 141 | namespace HelperClassBefore 142 | { 143 | //^6 144 | class Superpower 145 | { 146 | protected: 147 | void playSound(SoundId sound, double volume) 148 | { 149 | // Code here... 150 | } 151 | 152 | void stopSound(SoundId sound) 153 | { 154 | // Code here... 155 | } 156 | 157 | void setVolume(SoundId sound) 158 | { 159 | // Code here... 160 | } 161 | 162 | // Sandbox method and other operations... 163 | }; 164 | //^6 165 | }; 166 | 167 | namespace HelperClassAfter 168 | { 169 | //^7 170 | class SoundPlayer 171 | { 172 | void playSound(SoundId sound, double volume) 173 | { 174 | // Code here... 175 | } 176 | 177 | void stopSound(SoundId sound) 178 | { 179 | // Code here... 180 | } 181 | 182 | void setVolume(SoundId sound) 183 | { 184 | // Code here... 185 | } 186 | }; 187 | //^7 188 | 189 | //^8 190 | class Superpower 191 | { 192 | protected: 193 | SoundPlayer& getSoundPlayer() 194 | { 195 | return soundPlayer_; 196 | } 197 | 198 | // Sandbox method and other operations... 199 | 200 | private: 201 | SoundPlayer soundPlayer_; 202 | }; 203 | //^8 204 | } 205 | 206 | namespace PassToConstructor 207 | { 208 | class ParticleSystem {}; 209 | 210 | //^pass-to-ctor-base 211 | class Superpower 212 | { 213 | public: 214 | Superpower(ParticleSystem* particles) 215 | : particles_(particles) 216 | {} 217 | 218 | // Sandbox method and other operations... 219 | 220 | private: 221 | ParticleSystem* particles_; 222 | }; 223 | //^pass-to-ctor-base 224 | 225 | //^pass-to-ctor-sub 226 | class SkyLaunch : public Superpower 227 | { 228 | public: 229 | SkyLaunch(ParticleSystem* particles) 230 | : Superpower(particles) 231 | {} 232 | }; 233 | //^pass-to-ctor-sub 234 | } 235 | 236 | namespace TwoStageInit 237 | { 238 | class ParticleSystem {}; 239 | 240 | class Superpower 241 | { 242 | public: 243 | void init(ParticleSystem* particles) {} 244 | }; 245 | 246 | ParticleSystem* particles; 247 | 248 | class SkyLaunch : public Superpower {}; 249 | 250 | void foo() 251 | { 252 | //^9 253 | Superpower* power = new SkyLaunch(); 254 | power->init(particles); 255 | //^9 256 | } 257 | } 258 | 259 | namespace TwoStageInitEncapsulated 260 | { 261 | class ParticleSystem {}; 262 | 263 | class Superpower 264 | { 265 | public: 266 | void init(ParticleSystem* audio) {} 267 | }; 268 | 269 | class SkyLaunch : public Superpower {}; 270 | 271 | //^10 272 | Superpower* createSkyLaunch(ParticleSystem* particles) 273 | { 274 | Superpower* power = new SkyLaunch(); 275 | power->init(particles); 276 | return power; 277 | } 278 | //^10 279 | } 280 | 281 | namespace StaticState 282 | { 283 | class ParticleSystem {}; 284 | 285 | //^11 286 | class Superpower 287 | { 288 | public: 289 | static void init(ParticleSystem* particles) 290 | { 291 | particles_ = particles; 292 | } 293 | 294 | // Sandbox method and other operations... 295 | 296 | private: 297 | static ParticleSystem* particles_; 298 | }; 299 | //^11 300 | } 301 | 302 | namespace UseServiceLocator 303 | { 304 | struct ParticleSystem 305 | { 306 | void spawn(ParticleType type, int count); 307 | }; 308 | 309 | ParticleSystem particles; 310 | 311 | class Locator 312 | { 313 | public: 314 | static ParticleSystem& getParticles() { return particles; } 315 | }; 316 | 317 | //^12 318 | class Superpower 319 | { 320 | protected: 321 | void spawnParticles(ParticleType type, int count) 322 | { 323 | ParticleSystem& particles = Locator::getParticles(); 324 | particles.spawn(type, count); 325 | } 326 | 327 | // Sandbox method and other operations... 328 | }; 329 | //^12 330 | } 331 | -------------------------------------------------------------------------------- /code/cpp/type-object.h: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | namespace Subclasses 4 | { 5 | //^1 6 | class Monster 7 | { 8 | public: 9 | virtual ~Monster() {} 10 | virtual const char* getAttack() = 0; 11 | 12 | protected: 13 | Monster(int startingHealth) 14 | : health_(startingHealth) 15 | {} 16 | 17 | private: 18 | int health_; // Current health. 19 | }; 20 | //^1 21 | 22 | //^2 23 | class Dragon : public Monster 24 | { 25 | public: 26 | Dragon() : Monster(230) {} 27 | 28 | virtual const char* getAttack() 29 | { 30 | return "The dragon breathes fire!"; 31 | } 32 | }; 33 | 34 | class Troll : public Monster 35 | { 36 | public: 37 | Troll() : Monster(48) {} 38 | 39 | virtual const char* getAttack() 40 | { 41 | return "The troll clubs you!"; 42 | } 43 | }; 44 | //^2 45 | } 46 | 47 | namespace NoInheritance 48 | { 49 | //^3 50 | class Breed 51 | { 52 | public: 53 | Breed(int health, const char* attack) 54 | : health_(health), 55 | attack_(attack) 56 | {} 57 | 58 | int getHealth() { return health_; } 59 | const char* getAttack() { return attack_; } 60 | 61 | private: 62 | int health_; // Starting health. 63 | const char* attack_; 64 | }; 65 | //^3 66 | 67 | //^4 68 | class Monster 69 | { 70 | public: 71 | Monster(Breed& breed) 72 | : health_(breed.getHealth()), 73 | breed_(breed) 74 | {} 75 | 76 | const char* getAttack() 77 | { 78 | return breed_.getAttack(); 79 | } 80 | 81 | private: 82 | int health_; // Current health. 83 | Breed& breed_; 84 | }; 85 | //^4 86 | 87 | void testUsage() 88 | { 89 | Breed& someBreed = *(new Breed(123, "attack")); 90 | 91 | //^7 92 | Monster* monster = new Monster(someBreed); 93 | //^7 94 | use(monster); 95 | } 96 | } 97 | 98 | namespace BreedCtor 99 | { 100 | class Breed; 101 | 102 | class Monster 103 | { 104 | public: 105 | Monster(Breed& breed) {} 106 | }; 107 | 108 | //^5 109 | class Breed 110 | { 111 | public: 112 | Monster* newMonster() { return new Monster(*this); } 113 | 114 | // Previous Breed code... 115 | //^omit 116 | Breed(int health, const char* attack) 117 | : health_(health), 118 | attack_(attack) 119 | {} 120 | 121 | int getHealth() { return health_; } 122 | const char* getAttack() { return attack_; } 123 | 124 | private: 125 | int health_; // Starting health. 126 | const char* attack_; 127 | //^omit 128 | }; 129 | //^5 130 | 131 | void testUsage() 132 | { 133 | Breed& someBreed = *(new Breed(1, "foo")); 134 | 135 | //^8 136 | Monster* monster = someBreed.newMonster(); 137 | //^8 138 | use(monster); 139 | } 140 | } 141 | 142 | namespace BreedCtorMonster 143 | { 144 | class Breed 145 | { 146 | public: 147 | int getHealth() { return 0; } 148 | const char* getAttack() { return "s"; } 149 | }; 150 | 151 | //^6 152 | class Monster 153 | { 154 | friend class Breed; 155 | 156 | public: 157 | const char* getAttack() { return breed_.getAttack(); } 158 | 159 | private: 160 | Monster(Breed& breed) 161 | : health_(breed.getHealth()), 162 | breed_(breed) 163 | {} 164 | 165 | int health_; // Current health. 166 | Breed& breed_; 167 | }; 168 | //^6 169 | } 170 | 171 | namespace Inheritance 172 | { 173 | //^9 174 | class Breed 175 | { 176 | public: 177 | Breed(Breed* parent, int health, const char* attack) 178 | : parent_(parent), 179 | health_(health), 180 | attack_(attack) 181 | {} 182 | 183 | int getHealth(); 184 | const char* getAttack(); 185 | 186 | private: 187 | Breed* parent_; 188 | int health_; // Starting health. 189 | const char* attack_; 190 | }; 191 | //^9 192 | 193 | //^10 194 | int Breed::getHealth() 195 | { 196 | // Override. 197 | if (health_ != 0 || parent_ == NULL) return health_; 198 | 199 | // Inherit. 200 | return parent_->getHealth(); 201 | } 202 | 203 | const char* Breed::getAttack() 204 | { 205 | // Override. 206 | if (attack_ != NULL || parent_ == NULL) return attack_; 207 | 208 | // Inherit. 209 | return parent_->getAttack(); 210 | } 211 | //^10 212 | } 213 | 214 | namespace CopyDown 215 | { 216 | class Breed 217 | { 218 | public: 219 | //^copy-down 220 | Breed(Breed* parent, int health, const char* attack) 221 | : health_(health), 222 | attack_(attack) 223 | { 224 | // Inherit non-overridden attributes. 225 | if (parent != NULL) 226 | { 227 | if (health == 0) health_ = parent->getHealth(); 228 | if (attack == NULL) attack_ = parent->getAttack(); 229 | } 230 | } 231 | //^copy-down 232 | 233 | //^copy-down-access 234 | int getHealth() { return health_; } 235 | const char* getAttack() { return attack_; } 236 | //^copy-down-access 237 | 238 | private: 239 | int health_; // Starting health. 240 | const char* attack_; 241 | }; 242 | } 243 | 244 | namespace ExposeBreed 245 | { 246 | class Breed; 247 | 248 | //^11 249 | class Monster 250 | { 251 | public: 252 | Breed& getBreed() { return breed_; } 253 | 254 | // Existing code... 255 | //^omit 256 | Breed& breed_; 257 | //^omit 258 | }; 259 | //^11 260 | } 261 | 262 | namespace OverrideAttack 263 | { 264 | class Breed 265 | { 266 | public: 267 | Breed(const char* attack) 268 | : attack_(attack) 269 | {} 270 | 271 | const char* getAttack() { return attack_; } 272 | 273 | private: 274 | const char* attack_; 275 | }; 276 | 277 | class Monster 278 | { 279 | public: 280 | Monster(Breed& breed) 281 | : breed_(breed) 282 | {} 283 | 284 | const char* getAttack(); 285 | 286 | private: 287 | int health_; 288 | Breed& breed_; 289 | }; 290 | 291 | #define LOW_HEALTH 1 292 | 293 | //^12 294 | const char* Monster::getAttack() 295 | { 296 | if (health_ < LOW_HEALTH) 297 | { 298 | return "The monster flails weakly."; 299 | } 300 | 301 | return breed_.getAttack(); 302 | } 303 | //^12 304 | } -------------------------------------------------------------------------------- /code/cpp/update-method.h: -------------------------------------------------------------------------------- 1 | // 2 | // update-method.h 3 | // cpp 4 | // 5 | // Created by Bob Nystrom on 8/6/13. 6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved. 7 | // 8 | 9 | #ifndef cpp_update_method_h 10 | #define cpp_update_method_h 11 | 12 | namespace UpdateMethod 13 | { 14 | namespace Motivation 15 | { 16 | struct Entity 17 | { 18 | void shootLightning() {} 19 | void setX(double x) {} 20 | }; 21 | 22 | void refreshGame() {} 23 | 24 | void justPatrol() 25 | { 26 | Entity skeleton; 27 | 28 | //^just-patrol 29 | while (true) 30 | { 31 | // Patrol right. 32 | for (double x = 0; x < 100; x++) 33 | { 34 | skeleton.setX(x); 35 | } 36 | 37 | // Patrol left. 38 | for (double x = 100; x > 0; x--) 39 | { 40 | skeleton.setX(x); 41 | } 42 | } 43 | //^just-patrol 44 | } 45 | 46 | void patrolInLoop() 47 | { 48 | //^patrol-in-loop 49 | Entity skeleton; 50 | bool patrollingLeft = false; 51 | double x = 0; 52 | 53 | // Main game loop: 54 | while (true) 55 | { 56 | if (patrollingLeft) 57 | { 58 | x--; 59 | if (x == 0) patrollingLeft = false; 60 | } 61 | else 62 | { 63 | x++; 64 | if (x == 100) patrollingLeft = true; 65 | } 66 | 67 | skeleton.setX(x); 68 | 69 | // Handle user input and render game... 70 | } 71 | //^patrol-in-loop 72 | } 73 | 74 | void statues() 75 | { 76 | //^statues 77 | // Skeleton variables... 78 | Entity leftStatue; 79 | Entity rightStatue; 80 | int leftStatueFrames = 0; 81 | int rightStatueFrames = 0; 82 | 83 | // Main game loop: 84 | while (true) 85 | { 86 | // Skeleton code... 87 | 88 | if (++leftStatueFrames == 90) 89 | { 90 | leftStatueFrames = 0; 91 | leftStatue.shootLightning(); 92 | } 93 | 94 | if (++rightStatueFrames == 80) 95 | { 96 | rightStatueFrames = 0; 97 | rightStatue.shootLightning(); 98 | } 99 | 100 | // Handle user input and render game... 101 | } 102 | //^statues 103 | } 104 | } 105 | 106 | namespace KeepInMind 107 | { 108 | struct Entity 109 | { 110 | void setPosition(int x, int y) {} 111 | void shootLightning() {} 112 | void update() {} 113 | }; 114 | 115 | static const int MAX_ENTITIES = 10; 116 | 117 | void refreshGame() {} 118 | 119 | void skipAdded() 120 | { 121 | int numObjects_ = 0; 122 | Entity* objects_[MAX_ENTITIES]; 123 | //^skip-added 124 | int numObjectsThisTurn = numObjects_; 125 | for (int i = 0; i < numObjectsThisTurn; i++) 126 | { 127 | objects_[i]->update(); 128 | } 129 | //^skip-added 130 | } 131 | 132 | void skipRemoved() 133 | { 134 | int numObjects_ = 0; 135 | Entity* objects_[MAX_ENTITIES]; 136 | 137 | //^skip-removed 138 | for (int i = 0; i < numObjects_; i++) 139 | { 140 | objects_[i]->update(); 141 | } 142 | //^skip-removed 143 | } 144 | } 145 | 146 | namespace SampleCode 147 | { 148 | static const int MAX_ENTITIES = 10; 149 | 150 | //^entity-class 151 | class Entity 152 | { 153 | public: 154 | Entity() 155 | : x_(0), y_(0) 156 | {} 157 | 158 | virtual ~Entity() {} 159 | virtual void update() = 0; 160 | 161 | double x() const { return x_; } 162 | double y() const { return y_; } 163 | 164 | void setX(double x) { x_ = x; } 165 | void setY(double y) { y_ = y; } 166 | 167 | private: 168 | double x_; 169 | double y_; 170 | }; 171 | //^entity-class 172 | 173 | //^game-world 174 | class World 175 | { 176 | public: 177 | World() 178 | : numEntities_(0) 179 | {} 180 | 181 | void gameLoop(); 182 | 183 | private: 184 | Entity* entities_[MAX_ENTITIES]; 185 | int numEntities_; 186 | }; 187 | //^game-world 188 | 189 | //^game-loop 190 | void World::gameLoop() 191 | { 192 | while (true) 193 | { 194 | // Handle user input... 195 | 196 | // Update each entity. 197 | //^update-component-entities 198 | for (int i = 0; i < numEntities_; i++) 199 | { 200 | entities_[i]->update(); 201 | } 202 | //^update-component-entities 203 | 204 | // Physics and rendering... 205 | } 206 | } 207 | //^game-loop 208 | 209 | //^skeleton 210 | class Skeleton : public Entity 211 | { 212 | public: 213 | Skeleton() 214 | : patrollingLeft_(false) 215 | {} 216 | 217 | virtual void update() 218 | { 219 | if (patrollingLeft_) 220 | { 221 | setX(x() - 1); 222 | if (x() == 0) patrollingLeft_ = false; 223 | } 224 | else 225 | { 226 | setX(x() + 1); 227 | if (x() == 100) patrollingLeft_ = true; 228 | } 229 | } 230 | 231 | private: 232 | bool patrollingLeft_; 233 | }; 234 | //^skeleton 235 | 236 | //^statue 237 | class Statue : public Entity 238 | { 239 | public: 240 | Statue(int delay) 241 | : frames_(0), 242 | delay_(delay) 243 | {} 244 | 245 | virtual void update() 246 | { 247 | if (++frames_ == delay_) 248 | { 249 | shootLightning(); 250 | 251 | // Reset the timer. 252 | frames_ = 0; 253 | } 254 | } 255 | 256 | private: 257 | int frames_; 258 | int delay_; 259 | 260 | void shootLightning() 261 | { 262 | // Shoot the lightning... 263 | } 264 | }; 265 | //^statue 266 | } 267 | 268 | namespace ForwardToDelegate 269 | { 270 | class Entity; 271 | 272 | class Entity 273 | { 274 | public: 275 | Entity* state_; 276 | void update(); 277 | }; 278 | 279 | //^forward 280 | void Entity::update() 281 | { 282 | // Forward to state object. 283 | state_->update(); 284 | } 285 | //^forward 286 | } 287 | 288 | namespace VariableTimeStep 289 | { 290 | class Skeleton 291 | { 292 | public: 293 | double x; 294 | bool patrollingLeft_; 295 | void update(double elapsed); 296 | }; 297 | 298 | //^variable 299 | void Skeleton::update(double elapsed) 300 | { 301 | if (patrollingLeft_) 302 | { 303 | x -= elapsed; 304 | if (x <= 0) 305 | { 306 | patrollingLeft_ = false; 307 | x = -x; 308 | } 309 | } 310 | else 311 | { 312 | x += elapsed; 313 | if (x >= 100) 314 | { 315 | patrollingLeft_ = true; 316 | x = 100 - (x - 100); 317 | } 318 | } 319 | } 320 | //^variable 321 | } 322 | 323 | } 324 | 325 | #endif 326 | -------------------------------------------------------------------------------- /code/dart/spatial-partition/pigeonhole_comparisons.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | const MAX_POS = 100; 4 | 5 | const NUM_UNITS = 200; 6 | const NUM_RUNS = 100; 7 | 8 | final rand = new Random(); 9 | 10 | class Unit { 11 | final int pos; 12 | Unit(this.pos); 13 | } 14 | 15 | List makeUnits(int seed) { 16 | var rand = new Random(seed); 17 | 18 | var units = []; 19 | for (var i = 0; i < NUM_UNITS; i++) { 20 | units.add(new Unit(rand.nextInt(MAX_POS))); 21 | } 22 | 23 | return units; 24 | } 25 | 26 | slow() { 27 | var compares = 0; 28 | var hits = 0; 29 | 30 | for (var i = 0; i < NUM_RUNS; i++) { 31 | var units = makeUnits(i); 32 | 33 | for (var a = 0; a < units.length - 1; a++) { 34 | for (var b = a + 1; b < units.length; b++) { 35 | compares++; 36 | if (units[a].pos == units[b].pos) { 37 | hits++; 38 | } 39 | } 40 | } 41 | } 42 | 43 | compares /= NUM_RUNS; 44 | hits /= NUM_RUNS; 45 | print("slow: $compares compares, $hits hits"); 46 | } 47 | 48 | fast() { 49 | var compares = 0; 50 | var hits = 0; 51 | 52 | for (var i = 0; i < NUM_RUNS; i++) { 53 | var units = makeUnits(i); 54 | 55 | var slots = new List.generate(MAX_POS, (_) => []); 56 | for (var unit in units) slots[unit.pos].add(unit); 57 | 58 | for (var slot in slots) { 59 | for (var a = 0; a < slot.length - 1; a++) { 60 | for (var b = a + 1; b < slot.length; b++) { 61 | compares++; 62 | hits++; 63 | } 64 | } 65 | } 66 | } 67 | 68 | compares /= NUM_RUNS; 69 | hits /= NUM_RUNS; 70 | print("fast: $compares compares, $hits hits"); 71 | } 72 | 73 | main() { 74 | slow(); 75 | fast(); 76 | } -------------------------------------------------------------------------------- /code/flyweight/tiles/tiles.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2985588017E5645E00BA78C0 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2985587F17E5645E00BA78C0 /* main.cpp */; }; 11 | 2985588217E5645E00BA78C0 /* tiles.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2985588117E5645E00BA78C0 /* tiles.1 */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 2985587A17E5645E00BA78C0 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = /usr/share/man/man1/; 19 | dstSubfolderSpec = 0; 20 | files = ( 21 | 2985588217E5645E00BA78C0 /* tiles.1 in CopyFiles */, 22 | ); 23 | runOnlyForDeploymentPostprocessing = 1; 24 | }; 25 | /* End PBXCopyFilesBuildPhase section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 2985587C17E5645E00BA78C0 /* tiles */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tiles; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 2985587F17E5645E00BA78C0 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 30 | 2985588117E5645E00BA78C0 /* tiles.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = tiles.1; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 2985587917E5645E00BA78C0 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | 2985587317E5645E00BA78C0 = { 45 | isa = PBXGroup; 46 | children = ( 47 | 2985587E17E5645E00BA78C0 /* tiles */, 48 | 2985587D17E5645E00BA78C0 /* Products */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | 2985587D17E5645E00BA78C0 /* Products */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | 2985587C17E5645E00BA78C0 /* tiles */, 56 | ); 57 | name = Products; 58 | sourceTree = ""; 59 | }; 60 | 2985587E17E5645E00BA78C0 /* tiles */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 2985587F17E5645E00BA78C0 /* main.cpp */, 64 | 2985588117E5645E00BA78C0 /* tiles.1 */, 65 | ); 66 | path = tiles; 67 | sourceTree = ""; 68 | }; 69 | /* End PBXGroup section */ 70 | 71 | /* Begin PBXNativeTarget section */ 72 | 2985587B17E5645E00BA78C0 /* tiles */ = { 73 | isa = PBXNativeTarget; 74 | buildConfigurationList = 2985588517E5645E00BA78C0 /* Build configuration list for PBXNativeTarget "tiles" */; 75 | buildPhases = ( 76 | 2985587817E5645E00BA78C0 /* Sources */, 77 | 2985587917E5645E00BA78C0 /* Frameworks */, 78 | 2985587A17E5645E00BA78C0 /* CopyFiles */, 79 | ); 80 | buildRules = ( 81 | ); 82 | dependencies = ( 83 | ); 84 | name = tiles; 85 | productName = tiles; 86 | productReference = 2985587C17E5645E00BA78C0 /* tiles */; 87 | productType = "com.apple.product-type.tool"; 88 | }; 89 | /* End PBXNativeTarget section */ 90 | 91 | /* Begin PBXProject section */ 92 | 2985587417E5645E00BA78C0 /* Project object */ = { 93 | isa = PBXProject; 94 | attributes = { 95 | LastUpgradeCheck = 0460; 96 | ORGANIZATIONNAME = "Bob Nystrom"; 97 | }; 98 | buildConfigurationList = 2985587717E5645E00BA78C0 /* Build configuration list for PBXProject "tiles" */; 99 | compatibilityVersion = "Xcode 3.2"; 100 | developmentRegion = English; 101 | hasScannedForEncodings = 0; 102 | knownRegions = ( 103 | en, 104 | ); 105 | mainGroup = 2985587317E5645E00BA78C0; 106 | productRefGroup = 2985587D17E5645E00BA78C0 /* Products */; 107 | projectDirPath = ""; 108 | projectRoot = ""; 109 | targets = ( 110 | 2985587B17E5645E00BA78C0 /* tiles */, 111 | ); 112 | }; 113 | /* End PBXProject section */ 114 | 115 | /* Begin PBXSourcesBuildPhase section */ 116 | 2985587817E5645E00BA78C0 /* Sources */ = { 117 | isa = PBXSourcesBuildPhase; 118 | buildActionMask = 2147483647; 119 | files = ( 120 | 2985588017E5645E00BA78C0 /* main.cpp in Sources */, 121 | ); 122 | runOnlyForDeploymentPostprocessing = 0; 123 | }; 124 | /* End PBXSourcesBuildPhase section */ 125 | 126 | /* Begin XCBuildConfiguration section */ 127 | 2985588317E5645E00BA78C0 /* Debug */ = { 128 | isa = XCBuildConfiguration; 129 | buildSettings = { 130 | ALWAYS_SEARCH_USER_PATHS = NO; 131 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 132 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 133 | CLANG_CXX_LIBRARY = "libc++"; 134 | CLANG_WARN_CONSTANT_CONVERSION = YES; 135 | CLANG_WARN_EMPTY_BODY = YES; 136 | CLANG_WARN_ENUM_CONVERSION = YES; 137 | CLANG_WARN_INT_CONVERSION = YES; 138 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 139 | COPY_PHASE_STRIP = NO; 140 | GCC_C_LANGUAGE_STANDARD = gnu99; 141 | GCC_DYNAMIC_NO_PIC = NO; 142 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 143 | GCC_OPTIMIZATION_LEVEL = 0; 144 | GCC_PREPROCESSOR_DEFINITIONS = ( 145 | "DEBUG=1", 146 | "$(inherited)", 147 | ); 148 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 149 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 150 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 151 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 152 | GCC_WARN_UNUSED_VARIABLE = YES; 153 | MACOSX_DEPLOYMENT_TARGET = 10.8; 154 | ONLY_ACTIVE_ARCH = YES; 155 | SDKROOT = macosx; 156 | }; 157 | name = Debug; 158 | }; 159 | 2985588417E5645E00BA78C0 /* Release */ = { 160 | isa = XCBuildConfiguration; 161 | buildSettings = { 162 | ALWAYS_SEARCH_USER_PATHS = NO; 163 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 164 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 165 | CLANG_CXX_LIBRARY = "libc++"; 166 | CLANG_WARN_CONSTANT_CONVERSION = YES; 167 | CLANG_WARN_EMPTY_BODY = YES; 168 | CLANG_WARN_ENUM_CONVERSION = YES; 169 | CLANG_WARN_INT_CONVERSION = YES; 170 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 171 | COPY_PHASE_STRIP = YES; 172 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 173 | GCC_C_LANGUAGE_STANDARD = gnu99; 174 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 175 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 176 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 177 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 178 | GCC_WARN_UNUSED_VARIABLE = YES; 179 | MACOSX_DEPLOYMENT_TARGET = 10.8; 180 | SDKROOT = macosx; 181 | }; 182 | name = Release; 183 | }; 184 | 2985588617E5645E00BA78C0 /* Debug */ = { 185 | isa = XCBuildConfiguration; 186 | buildSettings = { 187 | GCC_ENABLE_CPP_EXCEPTIONS = NO; 188 | GCC_ENABLE_CPP_RTTI = NO; 189 | PRODUCT_NAME = "$(TARGET_NAME)"; 190 | }; 191 | name = Debug; 192 | }; 193 | 2985588717E5645E00BA78C0 /* Release */ = { 194 | isa = XCBuildConfiguration; 195 | buildSettings = { 196 | GCC_ENABLE_CPP_EXCEPTIONS = NO; 197 | GCC_ENABLE_CPP_RTTI = NO; 198 | PRODUCT_NAME = "$(TARGET_NAME)"; 199 | }; 200 | name = Release; 201 | }; 202 | /* End XCBuildConfiguration section */ 203 | 204 | /* Begin XCConfigurationList section */ 205 | 2985587717E5645E00BA78C0 /* Build configuration list for PBXProject "tiles" */ = { 206 | isa = XCConfigurationList; 207 | buildConfigurations = ( 208 | 2985588317E5645E00BA78C0 /* Debug */, 209 | 2985588417E5645E00BA78C0 /* Release */, 210 | ); 211 | defaultConfigurationIsVisible = 0; 212 | defaultConfigurationName = Release; 213 | }; 214 | 2985588517E5645E00BA78C0 /* Build configuration list for PBXNativeTarget "tiles" */ = { 215 | isa = XCConfigurationList; 216 | buildConfigurations = ( 217 | 2985588617E5645E00BA78C0 /* Debug */, 218 | 2985588717E5645E00BA78C0 /* Release */, 219 | ); 220 | defaultConfigurationIsVisible = 0; 221 | }; 222 | /* End XCConfigurationList section */ 223 | }; 224 | rootObject = 2985587417E5645E00BA78C0 /* Project object */; 225 | } 226 | -------------------------------------------------------------------------------- /code/flyweight/tiles/tiles/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // tiles 4 | // 5 | // Created by Bob Nystrom on 9/14/13. 6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | 12 | #define ADD_OTHER_STUFF 13 | 14 | static const int SIZE = 128; 15 | 16 | struct timeval startTime; 17 | 18 | void startProfile() 19 | { 20 | gettimeofday(&startTime, NULL); 21 | } 22 | 23 | void endProfile(const char* message) 24 | { 25 | struct timeval endTime; 26 | gettimeofday(&endTime, NULL); 27 | 28 | endTime.tv_sec -= startTime.tv_sec; 29 | endTime.tv_usec -= startTime.tv_usec; 30 | 31 | printf("%s: %lds %dμs\n", message, endTime.tv_sec, endTime.tv_usec); 32 | } 33 | 34 | int pickTile(int x, int y) { 35 | return (x * 7 + y * 13) % 4; 36 | } 37 | 38 | namespace tileEnum 39 | { 40 | enum Tile 41 | { 42 | TILE_GRASS = 0, 43 | TILE_FOREST, 44 | TILE_MOUNTAIN, 45 | TILE_WATER 46 | }; 47 | 48 | struct World 49 | { 50 | Tile tiles[SIZE * SIZE]; 51 | World() 52 | { 53 | for (int y = 0; y < SIZE; y++) 54 | { 55 | for (int x = 0; x < SIZE; x++) 56 | { 57 | tiles[y * SIZE + x] = (Tile)pickTile(x, y); 58 | } 59 | } 60 | } 61 | 62 | int addTiles() 63 | { 64 | int sum = 0; 65 | for (int y = 0; y < SIZE; y++) 66 | { 67 | for (int x = 0; x < SIZE; x++) 68 | { 69 | switch (tiles[y * SIZE + x]) 70 | { 71 | case TILE_GRASS: sum += 1; break; 72 | case TILE_FOREST: sum += 2; break; 73 | case TILE_MOUNTAIN: sum += 3; break; 74 | case TILE_WATER: sum += 4; break; 75 | } 76 | } 77 | } 78 | return sum; 79 | } 80 | }; 81 | }; 82 | 83 | namespace tileByte 84 | { 85 | enum Tile 86 | { 87 | TILE_GRASS = 0, 88 | TILE_FOREST, 89 | TILE_MOUNTAIN, 90 | TILE_WATER 91 | }; 92 | 93 | struct World 94 | { 95 | unsigned char tiles[SIZE * SIZE]; 96 | World() 97 | { 98 | for (int y = 0; y < SIZE; y++) 99 | { 100 | for (int x = 0; x < SIZE; x++) 101 | { 102 | tiles[y * SIZE + x] = pickTile(x, y); 103 | } 104 | } 105 | } 106 | 107 | int addTiles() 108 | { 109 | int sum = 0; 110 | for (int y = 0; y < SIZE; y++) 111 | { 112 | for (int x = 0; x < SIZE; x++) 113 | { 114 | switch (tiles[y * SIZE + x]) 115 | { 116 | case TILE_GRASS: sum += 1; break; 117 | case TILE_FOREST: sum += 2; break; 118 | case TILE_MOUNTAIN: sum += 3; break; 119 | case TILE_WATER: sum += 4; break; 120 | } 121 | } 122 | } 123 | return sum; 124 | } 125 | }; 126 | }; 127 | 128 | namespace tileField 129 | { 130 | struct Tile 131 | { 132 | int weight; 133 | #ifdef ADD_OTHER_STUFF 134 | int other[16]; 135 | #endif 136 | }; 137 | 138 | struct World 139 | { 140 | Tile tileTypes[4]; 141 | 142 | Tile* tiles[SIZE * SIZE]; 143 | World() 144 | { 145 | tileTypes[0].weight = 1; 146 | tileTypes[1].weight = 2; 147 | tileTypes[2].weight = 3; 148 | tileTypes[3].weight = 4; 149 | 150 | for (int y = 0; y < SIZE; y++) 151 | { 152 | for (int x = 0; x < SIZE; x++) 153 | { 154 | tiles[y * SIZE + x] = &tileTypes[pickTile(x, y)]; 155 | } 156 | } 157 | } 158 | 159 | int addTiles() 160 | { 161 | int sum = 0; 162 | for (int y = 0; y < SIZE; y++) 163 | { 164 | for (int x = 0; x < SIZE; x++) 165 | { 166 | sum += tiles[y * SIZE + x]->weight; 167 | } 168 | } 169 | return sum; 170 | } 171 | }; 172 | }; 173 | 174 | namespace tileVirtual 175 | { 176 | struct Tile 177 | { 178 | virtual int weight() = 0; 179 | }; 180 | 181 | struct GrassTile : public Tile 182 | { 183 | virtual int weight() { return 1; } 184 | }; 185 | 186 | struct ForestTile : public Tile 187 | { 188 | virtual int weight() { return 2; } 189 | }; 190 | 191 | struct MountainTile : public Tile 192 | { 193 | virtual int weight() { return 3; } 194 | }; 195 | 196 | struct WaterTile : public Tile 197 | { 198 | virtual int weight() { return 4; } 199 | }; 200 | 201 | struct World 202 | { 203 | GrassTile grass; 204 | ForestTile forest; 205 | MountainTile mountain; 206 | WaterTile water; 207 | Tile* tileTypes[4]; 208 | 209 | Tile* tiles[SIZE * SIZE]; 210 | World() 211 | { 212 | tileTypes[0] = &grass; 213 | tileTypes[1] = &forest; 214 | tileTypes[2] = &mountain; 215 | tileTypes[3] = &water; 216 | 217 | for (int y = 0; y < SIZE; y++) 218 | { 219 | for (int x = 0; x < SIZE; x++) 220 | { 221 | tiles[y * SIZE + x] = tileTypes[pickTile(x, y)]; 222 | } 223 | } 224 | } 225 | 226 | int addTiles() 227 | { 228 | int sum = 0; 229 | for (int y = 0; y < SIZE; y++) 230 | { 231 | for (int x = 0; x < SIZE; x++) 232 | { 233 | sum += tiles[y * SIZE + x]->weight(); 234 | } 235 | } 236 | return sum; 237 | } 238 | }; 239 | }; 240 | 241 | int main(int argc, const char * argv[]) 242 | { 243 | for (int i = 0; i < 1; i++) 244 | { 245 | { 246 | tileEnum::World world; 247 | startProfile(); 248 | int sum = world.addTiles(); 249 | endProfile("tile enum"); 250 | printf("%d\n", sum); 251 | } 252 | 253 | { 254 | tileByte::World world; 255 | startProfile(); 256 | int sum = world.addTiles(); 257 | endProfile("tile byte"); 258 | printf("%d\n", sum); 259 | } 260 | 261 | { 262 | tileField::World world; 263 | startProfile(); 264 | int sum = world.addTiles(); 265 | endProfile("tile field"); 266 | printf("%d\n", sum); 267 | } 268 | 269 | { 270 | tileVirtual::World world; 271 | startProfile(); 272 | int sum = world.addTiles(); 273 | endProfile("tile virtual"); 274 | printf("%d\n", sum); 275 | } 276 | } 277 | 278 | return 0; 279 | } 280 | -------------------------------------------------------------------------------- /code/flyweight/tiles/tiles/tiles.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 9/14/13 \" DATE 7 | .Dt tiles 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm tiles, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner -------------------------------------------------------------------------------- /code/structure-of-arrays/activate/activate.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 296232521830ACD100407933 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 296232511830ACD100407933 /* main.cpp */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | 296232431830ACB800407933 /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = /usr/share/man/man1/; 18 | dstSubfolderSpec = 0; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 1; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 296232451830ACB800407933 /* activate */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = activate; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 296232511830ACD100407933 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 28 | 296232531830ACED00407933 /* utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = utils.h; path = ../shared/utils.h; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 296232421830ACB800407933 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 2962323C1830ACB800407933 = { 43 | isa = PBXGroup; 44 | children = ( 45 | 296232531830ACED00407933 /* utils.h */, 46 | 296232511830ACD100407933 /* main.cpp */, 47 | 296232461830ACB800407933 /* Products */, 48 | ); 49 | sourceTree = ""; 50 | }; 51 | 296232461830ACB800407933 /* Products */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | 296232451830ACB800407933 /* activate */, 55 | ); 56 | name = Products; 57 | sourceTree = ""; 58 | }; 59 | /* End PBXGroup section */ 60 | 61 | /* Begin PBXNativeTarget section */ 62 | 296232441830ACB800407933 /* activate */ = { 63 | isa = PBXNativeTarget; 64 | buildConfigurationList = 2962324E1830ACB800407933 /* Build configuration list for PBXNativeTarget "activate" */; 65 | buildPhases = ( 66 | 296232411830ACB800407933 /* Sources */, 67 | 296232421830ACB800407933 /* Frameworks */, 68 | 296232431830ACB800407933 /* CopyFiles */, 69 | ); 70 | buildRules = ( 71 | ); 72 | dependencies = ( 73 | ); 74 | name = activate; 75 | productName = activate; 76 | productReference = 296232451830ACB800407933 /* activate */; 77 | productType = "com.apple.product-type.tool"; 78 | }; 79 | /* End PBXNativeTarget section */ 80 | 81 | /* Begin PBXProject section */ 82 | 2962323D1830ACB800407933 /* Project object */ = { 83 | isa = PBXProject; 84 | attributes = { 85 | LastUpgradeCheck = 0500; 86 | ORGANIZATIONNAME = "Bob Nystrom"; 87 | }; 88 | buildConfigurationList = 296232401830ACB800407933 /* Build configuration list for PBXProject "activate" */; 89 | compatibilityVersion = "Xcode 3.2"; 90 | developmentRegion = English; 91 | hasScannedForEncodings = 0; 92 | knownRegions = ( 93 | en, 94 | ); 95 | mainGroup = 2962323C1830ACB800407933; 96 | productRefGroup = 296232461830ACB800407933 /* Products */; 97 | projectDirPath = ""; 98 | projectRoot = ""; 99 | targets = ( 100 | 296232441830ACB800407933 /* activate */, 101 | ); 102 | }; 103 | /* End PBXProject section */ 104 | 105 | /* Begin PBXSourcesBuildPhase section */ 106 | 296232411830ACB800407933 /* Sources */ = { 107 | isa = PBXSourcesBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | 296232521830ACD100407933 /* main.cpp in Sources */, 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | /* End PBXSourcesBuildPhase section */ 115 | 116 | /* Begin XCBuildConfiguration section */ 117 | 2962324C1830ACB800407933 /* Debug */ = { 118 | isa = XCBuildConfiguration; 119 | buildSettings = { 120 | ALWAYS_SEARCH_USER_PATHS = NO; 121 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 122 | CLANG_CXX_LIBRARY = "libc++"; 123 | CLANG_ENABLE_OBJC_ARC = YES; 124 | CLANG_WARN_BOOL_CONVERSION = YES; 125 | CLANG_WARN_CONSTANT_CONVERSION = YES; 126 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 127 | CLANG_WARN_EMPTY_BODY = YES; 128 | CLANG_WARN_ENUM_CONVERSION = YES; 129 | CLANG_WARN_INT_CONVERSION = YES; 130 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 131 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 132 | COPY_PHASE_STRIP = NO; 133 | GCC_C_LANGUAGE_STANDARD = gnu99; 134 | GCC_DYNAMIC_NO_PIC = NO; 135 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 136 | GCC_OPTIMIZATION_LEVEL = 0; 137 | GCC_PREPROCESSOR_DEFINITIONS = ( 138 | "DEBUG=1", 139 | "$(inherited)", 140 | ); 141 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 142 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 143 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 144 | GCC_WARN_UNDECLARED_SELECTOR = YES; 145 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 146 | GCC_WARN_UNUSED_FUNCTION = YES; 147 | GCC_WARN_UNUSED_VARIABLE = YES; 148 | MACOSX_DEPLOYMENT_TARGET = 10.8; 149 | ONLY_ACTIVE_ARCH = YES; 150 | SDKROOT = macosx; 151 | }; 152 | name = Debug; 153 | }; 154 | 2962324D1830ACB800407933 /* Release */ = { 155 | isa = XCBuildConfiguration; 156 | buildSettings = { 157 | ALWAYS_SEARCH_USER_PATHS = NO; 158 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 159 | CLANG_CXX_LIBRARY = "libc++"; 160 | CLANG_ENABLE_OBJC_ARC = YES; 161 | CLANG_WARN_BOOL_CONVERSION = YES; 162 | CLANG_WARN_CONSTANT_CONVERSION = YES; 163 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 164 | CLANG_WARN_EMPTY_BODY = YES; 165 | CLANG_WARN_ENUM_CONVERSION = YES; 166 | CLANG_WARN_INT_CONVERSION = YES; 167 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 168 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 169 | COPY_PHASE_STRIP = YES; 170 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 171 | ENABLE_NS_ASSERTIONS = NO; 172 | GCC_C_LANGUAGE_STANDARD = gnu99; 173 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 174 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 175 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 176 | GCC_WARN_UNDECLARED_SELECTOR = YES; 177 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 178 | GCC_WARN_UNUSED_FUNCTION = YES; 179 | GCC_WARN_UNUSED_VARIABLE = YES; 180 | MACOSX_DEPLOYMENT_TARGET = 10.8; 181 | SDKROOT = macosx; 182 | }; 183 | name = Release; 184 | }; 185 | 2962324F1830ACB800407933 /* Debug */ = { 186 | isa = XCBuildConfiguration; 187 | buildSettings = { 188 | PRODUCT_NAME = "$(TARGET_NAME)"; 189 | }; 190 | name = Debug; 191 | }; 192 | 296232501830ACB800407933 /* Release */ = { 193 | isa = XCBuildConfiguration; 194 | buildSettings = { 195 | PRODUCT_NAME = "$(TARGET_NAME)"; 196 | }; 197 | name = Release; 198 | }; 199 | /* End XCBuildConfiguration section */ 200 | 201 | /* Begin XCConfigurationList section */ 202 | 296232401830ACB800407933 /* Build configuration list for PBXProject "activate" */ = { 203 | isa = XCConfigurationList; 204 | buildConfigurations = ( 205 | 2962324C1830ACB800407933 /* Debug */, 206 | 2962324D1830ACB800407933 /* Release */, 207 | ); 208 | defaultConfigurationIsVisible = 0; 209 | defaultConfigurationName = Release; 210 | }; 211 | 2962324E1830ACB800407933 /* Build configuration list for PBXNativeTarget "activate" */ = { 212 | isa = XCConfigurationList; 213 | buildConfigurations = ( 214 | 2962324F1830ACB800407933 /* Debug */, 215 | 296232501830ACB800407933 /* Release */, 216 | ); 217 | defaultConfigurationIsVisible = 0; 218 | }; 219 | /* End XCConfigurationList section */ 220 | }; 221 | rootObject = 2962323D1830ACB800407933 /* Project object */; 222 | } 223 | -------------------------------------------------------------------------------- /code/structure-of-arrays/activate/main.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | static const int NUM_FRAMES = 1000; 4 | static const int NUM_ACTORS = 10000; 5 | static const int NUM_FIELDS = 4; 6 | static const int TOGGLE_RATE = 10; 7 | 8 | class Actor 9 | { 10 | public: 11 | Actor() 12 | { 13 | for (int i = 0; i < NUM_FIELDS; i++) data[i] = i; 14 | } 15 | 16 | bool isActive = false; 17 | 18 | int data[NUM_FIELDS]; 19 | 20 | int update() 21 | { 22 | int sum = 0; 23 | for (int i = 0; i < NUM_FIELDS; i++) sum += data[i]; 24 | return sum; 25 | } 26 | }; 27 | 28 | class SmallActor 29 | { 30 | public: 31 | SmallActor() 32 | { 33 | for (int i = 0; i < NUM_FIELDS; i++) data[i] = i; 34 | } 35 | 36 | int data[NUM_FIELDS]; 37 | 38 | int update() 39 | { 40 | int sum = 0; 41 | for (int i = 0; i < NUM_FIELDS; i++) sum += data[i]; 42 | return sum; 43 | } 44 | }; 45 | 46 | // A flat array of contiguous actors with randomly chosen ones active. 47 | void testMixed() 48 | { 49 | Actor* actors = new Actor[NUM_ACTORS]; 50 | 51 | // Enable half of them randomly. 52 | int* indexes = shuffledArray(NUM_ACTORS); 53 | for (int i = 0; i < NUM_ACTORS / 2; i++) 54 | { 55 | actors[indexes[i]].isActive = true; 56 | } 57 | delete [] indexes; 58 | 59 | long sum = 0; 60 | startProfile(); 61 | for (int i = 0; i < NUM_FRAMES; i++) 62 | { 63 | for (int j = 0; j < NUM_ACTORS; j++) 64 | { 65 | if (actors[j].isActive) 66 | { 67 | sum += actors[j].update(); 68 | } 69 | 70 | // Randomly change active state. 71 | if (randRange(1, TOGGLE_RATE) == 0) actors[j].isActive = !actors[j].isActive; 72 | } 73 | } 74 | endProfile(" mixed"); 75 | use(sum); 76 | 77 | delete [] actors; 78 | } 79 | 80 | // A flat array of contiguous actors with the active ones first. 81 | void testSorted() 82 | { 83 | SmallActor* actors = new SmallActor[NUM_ACTORS]; 84 | 85 | // Enable half of them. 86 | int numActive = NUM_ACTORS / 2; 87 | 88 | long sum = 0; 89 | startProfile(); 90 | for (int i = 0; i < NUM_FRAMES; i++) 91 | { 92 | for (int j = 0; j < numActive; j++) 93 | { 94 | sum += actors[j].update(); 95 | } 96 | 97 | for (int j = 0; j < NUM_ACTORS; j++) 98 | { 99 | // Randomly change active state. 100 | if (randRange(1, TOGGLE_RATE) == 0) 101 | { 102 | if (j < numActive) 103 | { 104 | // Inactive, so move it after all of the active ones. 105 | numActive--; 106 | } 107 | else 108 | { 109 | // Active, so move it to the end of the active section. 110 | numActive++; 111 | } 112 | SmallActor temp = actors[j]; 113 | actors[j] = actors[numActive]; 114 | actors[numActive] = temp; 115 | } 116 | } 117 | } 118 | endProfile("sorted"); 119 | use(sum); 120 | 121 | delete [] actors; 122 | } 123 | 124 | int main(int argc, const char * argv[]) 125 | { 126 | srand((unsigned int)time(0)); 127 | 128 | for (int i = 0; i < 4; i++) 129 | { 130 | testMixed(); 131 | testSorted(); 132 | } 133 | 134 | return 0; 135 | } 136 | 137 | -------------------------------------------------------------------------------- /code/structure-of-arrays/combined/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // combined 4 | // 5 | // Created by Bob Nystrom on 11/17/13. 6 | // Copyright (c) 2013 Bob Nystrom. All rights reserved. 7 | // 8 | 9 | #include 10 | 11 | #include "utils.h" 12 | 13 | static const int NUM_FIELDS = 4; 14 | static const int NUM_ACTORS = 2000; 15 | static const int NUM_COMPONENTS = 4; 16 | static const int NUM_FRAMES = 10000; 17 | static const int NUM_COLD_FIELDS = 16; 18 | static const int EXTRA = 10; 19 | 20 | class Component 21 | { 22 | public: 23 | int data[NUM_FIELDS]; 24 | 25 | Component() 26 | { 27 | for (int i = 0; i < NUM_FIELDS; i++) data[i] = i; 28 | } 29 | 30 | int update() 31 | { 32 | int sum = 0; 33 | for (int i = 0; i < NUM_FIELDS; i++) sum += data[i]; 34 | return sum; 35 | } 36 | }; 37 | 38 | class ActorBefore 39 | { 40 | public: 41 | bool isActive = true; 42 | 43 | Component* components[NUM_COMPONENTS]; 44 | int cold[NUM_COLD_FIELDS]; 45 | }; 46 | 47 | void testSlow(float best) 48 | { 49 | ActorBefore** actors = new ActorBefore*[NUM_ACTORS * EXTRA]; 50 | 51 | // Create a bunch of actors with random amounts of space between them. 52 | for (int i = 0; i < NUM_ACTORS * EXTRA; i++) 53 | { 54 | actors[i] = new ActorBefore(); 55 | } 56 | 57 | // Shuffle them in the list. 58 | shuffle(actors, NUM_ACTORS * EXTRA); 59 | 60 | // Deactivate half of them. 61 | for (int i = 0; i < NUM_ACTORS * EXTRA / 2; i++) 62 | { 63 | actors[i]->isActive = false; 64 | } 65 | 66 | // Shuffle them in the list. 67 | shuffle(actors, NUM_ACTORS * EXTRA); 68 | 69 | // Create a bunch of components. 70 | Component** components = new Component*[NUM_ACTORS * NUM_COMPONENTS * EXTRA]; 71 | for (int i = 0; i < NUM_ACTORS * NUM_COMPONENTS * EXTRA; i++) 72 | { 73 | components[i] = new Component(); 74 | } 75 | 76 | // Shuffle them in the list. 77 | shuffle(components, NUM_ACTORS * NUM_COMPONENTS * EXTRA); 78 | 79 | // Wire them up to actors. 80 | for (int i = 0; i < NUM_ACTORS; i++) 81 | { 82 | for (int j = 0; j < NUM_COMPONENTS; j++) 83 | { 84 | actors[i]->components[j] = components[i * NUM_COMPONENTS + j]; 85 | } 86 | } 87 | 88 | // Run the simulation. 89 | startProfile(); 90 | int sum = 0; 91 | for (int f = 0; f < NUM_FRAMES; f++) 92 | { 93 | for (int c = 0; c < NUM_COMPONENTS; c++) 94 | { 95 | for (int a = 0; a < NUM_ACTORS; a++) 96 | { 97 | if (!actors[a]->isActive) continue; 98 | sum += actors[a]->components[c]->update(); 99 | } 100 | } 101 | } 102 | endProfile("worst", best); 103 | if (sum != 240000000) printf("%d\n", sum); 104 | 105 | // Free everything. 106 | for (int i = 0; i < NUM_ACTORS; i++) 107 | { 108 | delete actors[i]; 109 | } 110 | 111 | for (int i = 0; i < NUM_ACTORS * NUM_COMPONENTS; i++) 112 | { 113 | delete components[i]; 114 | } 115 | 116 | delete [] actors; 117 | delete [] components; 118 | } 119 | 120 | float testFast() 121 | { 122 | Component* components[NUM_COMPONENTS]; 123 | 124 | // Create the component arrays. 125 | for (int i = 0; i < NUM_COMPONENTS; i++) 126 | { 127 | components[i] = new Component[NUM_ACTORS]; 128 | } 129 | 130 | // Run the simulation. 131 | startProfile(); 132 | int sum = 0; 133 | for (int f = 0; f < NUM_FRAMES; f++) 134 | { 135 | for (int c = 0; c < NUM_COMPONENTS; c++) 136 | { 137 | Component* componentArray = components[c]; 138 | for (int a = 0; a < NUM_ACTORS / 2; a++) 139 | { 140 | sum += componentArray[a].update(); 141 | } 142 | } 143 | } 144 | float result = endProfile("best"); 145 | if (sum != 240000000) printf("%d\n", sum); 146 | 147 | // Free everything. 148 | for (int i = 0; i < NUM_COMPONENTS; i++) 149 | { 150 | delete [] components[i]; 151 | } 152 | 153 | return result; 154 | } 155 | 156 | int main(int argc, const char * argv[]) 157 | { 158 | for (int i = 0; i < 3; i++) 159 | { 160 | float best = testFast(); 161 | testSlow(best); 162 | } 163 | return 0; 164 | } 165 | 166 | -------------------------------------------------------------------------------- /code/structure-of-arrays/random_array_access/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "utils.h" 4 | 5 | // Based on http://stackoverflow.com/questions/1126529/what-is-the-cost-of-an-l1-cache-miss 6 | // 7 | // Tries to show effects of cache misses by modifying elements of an array 8 | // twice, once in order and once in random order. 9 | // 10 | // On my machine, I get results like: 11 | // ordered: 43.9250ms 12 | // random: 308.4460ms 13 | // 7.02x 14 | // ordered: 14.6060ms 15 | // random: 314.2920ms 16 | // 21.52x 17 | // ordered: 14.3640ms 18 | // random: 304.5980ms 19 | // 21.21x 20 | // ordered: 14.3160ms 21 | // random: 316.0700ms 22 | // 22.08x 23 | // ordered: 15.6090ms 24 | // random: 317.1620ms 25 | // 20.32x 26 | // ordered: 15.7320ms 27 | // random: 281.0440ms 28 | // 17.86x 29 | 30 | void accessRandomArray(int iterations, int length) 31 | { 32 | srand((unsigned int)time(0)); 33 | 34 | // Allocate the data as one single chunk of memory. 35 | int* nodes = new int[length]; 36 | 37 | // Create array that give the access order for the data. Going through the 38 | // indirect for both the ordered and random traversals ensures the randomness 39 | // itself doesn't add any overhead. 40 | int* orderedIndexes = new int[length]; 41 | for (int i = 0; i < length; i++) 42 | { 43 | orderedIndexes[i] = i; 44 | } 45 | 46 | int* randomIndexes = shuffledArray(length); 47 | 48 | long sum = 0; 49 | 50 | // Run the whole thing a few times to make sure the numbers are stable. 51 | for (int a = 0; a < iterations; a++) 52 | { 53 | // Write the data in order. 54 | startProfile(); 55 | for (int i = 0; i < length; i++) nodes[orderedIndexes[i]] = i; 56 | float ordered = endProfile("ordered: "); 57 | 58 | // Actually use the data so the compiler doesn't optimize it to nothing. 59 | for (int i = 0; i < iterations; i++) sum += nodes[i]; 60 | 61 | // Now access the array positions in a "random" order. 62 | startProfile(); 63 | for (int i = 0; i < length; i++) nodes[randomIndexes[i]] = i; 64 | endProfile(" random: ", ordered); 65 | 66 | // Actually use the data so the compiler doesn't optimize it to nothing. 67 | for (int i = 0; i < iterations; i++) sum -= nodes[i]; 68 | } 69 | 70 | // Actually use the data so the compiler doesn't optimize it to nothing. 71 | if (sum != 0) printf("Got wrong sum %ld (should be 0)\n", sum); 72 | 73 | delete [] nodes; 74 | delete [] orderedIndexes; 75 | delete [] randomIndexes; 76 | } 77 | 78 | int main( int argc, char* argv[] ) 79 | { 80 | accessRandomArray(6, 20000000); 81 | } -------------------------------------------------------------------------------- /code/structure-of-arrays/shared/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | clock_t startTime; 9 | 10 | void startProfile() 11 | { 12 | startTime = clock(); 13 | } 14 | 15 | float endProfile(const char* message) 16 | { 17 | float elapsed = (float)(clock() - startTime) * 1000.0f / CLOCKS_PER_SEC; 18 | printf("%s%10.4fms\n", message, elapsed); 19 | return elapsed; 20 | } 21 | 22 | float endProfile(const char* message, float comparison) 23 | { 24 | float elapsed = (float)(clock() - startTime) * 1000.0f / CLOCKS_PER_SEC; 25 | printf("%s%10.4fms %6.2fx\n", message, elapsed, elapsed / comparison); 26 | return elapsed; 27 | } 28 | 29 | // Get random value in the given range (half-inclusive). 30 | int randRange(int m, int n) 31 | { 32 | return rand() % (n - m) + m; 33 | } 34 | 35 | // Make sure the code leading to the argument to this call isn't compiled out. 36 | void use(long sum) 37 | { 38 | if (sum == 123) printf("!"); 39 | } 40 | 41 | template 42 | T median(std::vector items) 43 | { 44 | std::sort(items.begin(), items.end()); 45 | if (items.size() % 2 == 0) 46 | { 47 | return (items[items.size() / 2] + items[items.size() / 2 + 1]) / 2; 48 | } 49 | else 50 | { 51 | return items[items.size() / 2]; 52 | } 53 | } 54 | 55 | int* shuffledArray(int length) 56 | { 57 | int* array = new int[length]; 58 | for (int i = 0; i < length; i++) array[i] = i; 59 | 60 | for (int i = 0; i < length; i++) 61 | { 62 | int j = randRange(i, length); 63 | int t = array[i]; 64 | array[i] = array[j]; 65 | array[j] = t; 66 | } 67 | 68 | return array; 69 | } 70 | 71 | template 72 | void shuffle(T* array, int length) 73 | { 74 | for (int i = 0; i < length; i++) 75 | { 76 | int j = randRange(i, length); 77 | T temp = array[i]; 78 | array[i] = array[j]; 79 | array[j] = temp; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /draft/outline/context-parameter.markdown: -------------------------------------------------------------------------------- 1 | ^title Context Parameter 2 | ^theme Behaving 3 | ^outline 4 | 5 | ## Intent 6 | *Define behavior in terms of operations on a given context object 7 | passed to the method.* 8 | 9 | ## Motivation 10 | Each entity defines a Render() function to draw the entity. In a 11 | naïve implementation, would call low-level render API directly. 12 | But that couples entity code to entire render API, some of which may 13 | be too low-level. 14 | 15 | Worse, in order to render, need to know right state. For example, 16 | local transform of entity, camera position, which buffer to render to, 17 | etc. 18 | 19 | We need a way to bundle this contextual information and have entity 20 | implement its rendering in terms of that context. 21 | 22 | ## The Pattern 23 | **Base class** defines a **contextual operation** as an abstract 24 | method that takes a **context object**. Context object maintains state 25 | needed to perform operations, and exposes **basic operations** that 26 | use it. Derived **concrete class** implements the contextual operation 27 | by calling methods on the context object. 28 | 29 | ## When to Use It 30 | * When the context and operations needed aren't already available 31 | either globally (see Service) or from the base class itself (see 32 | Subclass Sandbox). 33 | 34 | * When the context needed to perform an operation is not owned by the 35 | class defining the operation. (In our example, Entity doesn't own 36 | the render state.) 37 | 38 | ## Keep in Mind 39 | 40 | ## Design Decisions 41 | 42 | ## Sample Code 43 | 44 | ## See Also 45 | 46 | * Sandbox works similarly, but used protected methods in base class 47 | instead of passed in object. -------------------------------------------------------------------------------- /draft/outline/game-loop.markdown: -------------------------------------------------------------------------------- 1 | ^title Game Loop 2 | ^section Sequencing Patterns 3 | ^outline 4 | 5 | ## Intent 6 | 7 | *Simulate the progression of time and run smoothly independent of user input.* 8 | 9 | ## Motivation 10 | 11 | - old programs used to be batch: ran and quit 12 | - then came interactive programs. these ran forever, which meant loop 13 | - if you write gui app, the os has event loop 14 | - you receive ui event, handle it, and return to event loop 15 | - js is a great example 16 | - you don't see loop, but it's there 17 | - when app isn't handle user input, it isn't doing anything, just sitting 18 | there 19 | - this can work for simple games (minesweeper, solitaire) 20 | - game loop 21 | - games are living breathing worlds. need to keep simulating independent of 22 | user input. don't wait. 23 | - even turn based games often have animation that needs to keep going even 24 | when user isn't doing anything 25 | - solution is game does loop itself. polls for input instead of blocking 26 | 27 | ### Two Kinds of Time 28 | 29 | - two kinds of time: user's wall clock time. game's simulation time (ticks) 30 | - exchange rate between them: how much *work* can the cpu handle in a given 31 | amount of real time. old games could hard code exchange rate. 32 | - modern hardware is too flexible: run on range of devices, multitasking, 33 | battery life, etc. 34 | - need to dynamically calculate exchange rate 35 | 36 | - out of scope 37 | - networking and coordinating time 38 | - threading: sim on one, render on another, etc. 39 | 40 | ## The Pattern 41 | 42 | A **game loop** runs continuously until the game is exited. Each turn of the 43 | loop, it **reads user input**, then **updates the game state**, then **displays 44 | the game**. It will also keep track of the passage of time so that it can 45 | control the **rate of gameplay**. 46 | 47 | ## When to Use It 48 | 49 | One of the few things common to almost every game. You will almost always use 50 | it. 51 | 52 | Counterexample is non-realtime games that rely on OS GUI. If you write a 53 | Minesweeper clone, may not to code a game loop. Still there, though: it's the 54 | OS's event loop. 55 | 56 | may not write it yourself. if using game engine, engine will usually provide it. this is one of the main differences between a "library" and an "engine" or "framework". with engine, it owns the main loop and calls your code. lib is other way. game loop is drive shaft of engine. 57 | 58 | will likely even use this for turn-based games since animation, vfx, etc. is 59 | realtime. a turn-based game is just a realtime game where the enemy doesn't 60 | move until after the player does. 61 | 62 | ## Keep in Mind 63 | 64 | - need to coordinate with os' event loop 65 | - consider different hardware capabilities game will run on. the more known 66 | they are, the simpler your game loop 67 | - running as fast as possible on mobile can harm battery life 68 | 69 | ## Sample Code 70 | 71 | ### Ye Olde Game Loope 72 | 73 | main() 74 | while (notDeadYet) 75 | readInput() 76 | updateGame() 77 | render() 78 | 79 | fine in atari days where you knew exact hardware you were running on and could 80 | own entire cpu. 81 | 82 | bad these days. 83 | 84 | ### Fixing the Framerate 85 | 86 | 87 | 88 | - if machine is fast enough game runs at reliable speed 89 | - game still runs at right speed if machine is faster 90 | - battery-life friendly 91 | - game slows down if machine is too slow 92 | 93 | ### Decouple Update and Render Rate 94 | 95 | 96 | 97 | - uses max power 98 | - updates with fixed time step 99 | - can get into spiral of death 100 | - doesn't play more smoothly on better hardware 101 | 102 | ### Interpolated Rendering 103 | 104 | 105 | 106 | ## Design Decisions 107 | 108 | ### How does it adapt to fast hardware? 109 | 110 | ### How does it adapt to slow hardware? 111 | 112 | ### Does power consumption or CPU usage matter? 113 | 114 | - if the loop has sleep(), battery-friendly. if not, optimizes game experience 115 | 116 | ## See Also 117 | 118 | http://gafferongames.com/game-physics/fix-your-timestep/ 119 | http://www.koonsolo.com/news/dewitters-gameloop/ 120 | http://en.wikipedia.org/wiki/Game_programming 121 | http://active.tutsplus.com/tutorials/games/understanding-the-game-loop-basix/ 122 | http://www.mvps.org/directx/articles/writing_the_game_loop.htm 123 | http://entropyinteractive.com/2011/02/game-engine-design-the-game-loop/ 124 | http://www.nuclex.org/articles/3-basics/5-how-a-game-loop-works 125 | -------------------------------------------------------------------------------- /draft/outline/hello-world.markdown: -------------------------------------------------------------------------------- 1 | ^title Hello World 2 | 3 | Here's a general summary of the problem or problems that this pattern 4 | can solve. It's a fairly short introductory paragraph to the pattern. 5 | Note that it lacks a header. It ends with a single *emphasized 6 | summary of the pattern*. 7 | 8 | ## The Pattern 9 | This is the main expository section of the pattern. It explains what 10 | the pattern is in terms of an example application. Although it 11 | references an example, it does not use code to explain the pattern. 12 | This is still the pattern in fairly general terms. 13 | 14 | > This is a sidebar section. These can be used anywhere in the book 15 | > to provide additional, but not necessary, information about the 16 | > following passage. 17 | > 18 | > It has two paragraphs. 19 | 20 | This section will likely be a page or two long, and will often contain 21 | diagrams or other helpful illustrations. 22 | 23 | ## Implementation 24 | This longer section walks through the important skeleton of an 25 | implementation of the example pattern described in the previous 26 | section. It will not necessarily show *all* of the code required to 27 | implement the pattern, just enough to get the idea across. 28 | 29 | Because of the step-by-step nature of this section, and the frequent 30 | code samples, this will likely be the longer part of the chapter. 31 | 32 | Just for format testing, here's an ordered list: 33 | 34 | 1. This is the first item. It has a pretty long body of text so that 35 | we can ensure that text wrapping is working correctly. 36 | 37 | * And this is a nested list. It has a pretty long body of text so 38 | that we can ensure that text wrapping is working correctly. 39 | 40 | 1. Three levels of nesting should be plenty. Make it nice and 41 | long so we can ensure word wrap wraps at the right place. 42 | 43 | * Second nested item. 44 | 45 | 2. This is the second item. 46 | 47 | 3. This is the third item. 48 | 49 | Here is one code sample: 50 | 51 | ^code 1 52 | 53 | Just to test code inclusion and ensure it's working right, here's 54 | another: 55 | 56 | ^code 2 57 | 58 | ## Implications 59 | 60 | * In bullet form, lists the implications of using this pattern. 61 | 62 | * Includes pros. 63 | 64 | * Also includes cons. 65 | 66 | ## Applications 67 | This short section describes common uses of the pattern. It may be 68 | omitted if not useful. 69 | 70 | ## Related Patterns 71 | Patterns are often used in tandem or in place of other patterns. This 72 | section, if relevant, describes other patterns that often touch on 73 | this one. 74 | 75 | **How are refinements handled? Do they get all of the sections the 76 | full pattern gets?** 77 | -------------------------------------------------------------------------------- /draft/outline/introduction.markdown: -------------------------------------------------------------------------------- 1 | ^title Introduction 2 | 3 | * my personal history 4 | * taught myself to program 5 | * always wanted to write games, like every one else 6 | * could find resources for graphics, sound, etc. but struggled on 7 | personal projects. once they reached a certain size, they became 8 | unweildy messes and i gave up. 9 | * in 2001, got a job at ea. 10 | * couldn't wait to see how real game teams organized their 11 | codebases. 12 | * not what i was expecting: there were some architectural gems, 13 | but a lot of their codebases looked like my unweildy messes. 14 | they justthrew more bodies at the problem and soldiered on. 15 | * disappointing, but inspiring too: i know it can be done better. 16 | some parts of codebases i saw were really brilliant. why doesn't 17 | everyone know how to put things together like that? 18 | * this book tries to help that: get the smart things i saw some 19 | people doing and collect them together so that everyone else can 20 | learn from it. 21 | * what's in store 22 | * not like other game books: no graphics algorithms, etc. instead, 23 | provides so you can build an architecture to house those things. 24 | if a game is a house, graphics and sound are light fixtures and 25 | appliances. this book will teach you how to put in the wiring 26 | and plumbing. 27 | * this won't teach you how to write an entire game engine. but if 28 | you've ever worked on a game engine and wondered why it was so 29 | hard to add features to it, or change, this book aims to help. 30 | 31 | ## How it Relates to Design Patterns 32 | * this is essentially an extension of gof's book. while you don't 33 | necessarily need both, if i were you and i could only buy one of 34 | these books, i'd buy theirs. almost every pattern in design 35 | patterns can find an effective use inside a game engine. if you're 36 | doing significant architecture on a game, you should be familiar 37 | with them. 38 | * once you have those down, this book picks up from there with more 39 | patterns that are rarely encountered outside of games. 40 | * this also covers some patterns from the gof book, but with a 41 | clearer emphasis on how they can be used in games. if you know 42 | patterns, but have trouble knowing when to use them, this may 43 | help. 44 | * some patterns here are smaller in scale. design patterns tries 45 | to restrict itself to just architectural patterns. this book 46 | has those, but also smaller scale implementation patterns. 47 | 48 | ## How This Book is Structured and Formatted 49 | 50 | * overview and intro struff 51 | * if this were a recipe book (and it is), this would be how to 52 | measure ingredients, cut meat, etc. 53 | * how the book is intended to be used 54 | * how it relates to other material on patterns 55 | * how to apply it to your everyday programming 56 | * collection of patterns 57 | * these are the recipes 58 | * patterns are grouped into several themes. each theme describes 59 | two opposing forces that cause problems which must be reconciled 60 | to make a game. for each theme, several design patterns are 61 | provided. 62 | * each pattern is structured like 63 | * introduction: describes the problem the pattern solves 64 | * the pattern: using an example usage, describes each step and 65 | part of the pattern. no code samples yet. 66 | * implementation: demonstrates and explains a sample 67 | implementation of the previously described example. not all 68 | code is included, just the meat required to get the point 69 | across. 70 | * implications: the pros, cons, requirements and side-effects 71 | of using this pattern are explaining in a bullet list 72 | * applications: if useful, a short section explaining common 73 | places where this pattern is used. for example: the command 74 | pattern is often used for undo, etc. 75 | * related patterns: if the pattern is often used in 76 | conjunction with others, or otherwise references them, 77 | mention them here. 78 | * refinements: some patterns may be expanded upon or modified. 79 | if applicable, describe common refinements here. **still 80 | need to figure out exactly how this will be integrated into 81 | the overall pattern chapter.** 82 | * formatting 83 | * code is in a `monospace font` 84 | * material that may be interesting but isn't necessary to know may 85 | show up in sidebars 86 | * patterns have components. each component is in **bold face** 87 | when it is first defined 88 | * patterns described in this book are Capitalized (1) with the 89 | page number of the pattern's chapter after the name. 90 | 91 | ## Example Code 92 | 93 | * none of the patterns in this are particularly bound to a single 94 | programming language. whatever language you're using can probably 95 | handle them. so the choice of example language is kind of arbitrary. 96 | * sample code is in c++ 97 | * most game programmers know it, or at least know a language like 98 | Java or C# with similar syntax 99 | * consistent with GoF 100 | * some patterns deal specifically with memory or static type 101 | system limitations, and c++ lets you work in those domains. 102 | * complete working programs are not presented here. you're a smart 103 | coder and can fill in the blanks with obvious stuff. keeps us 104 | focused on the heart of the pattern. 105 | * the coding style is one that works best for a book, not for 106 | production code. may not be the best style guide. 107 | 108 | ## Evolving 109 | 110 | * patterns by nature are constantly growing and changing to reflect 111 | the community that uses them. your involvement is an important 112 | part of the process. if you have suggestions, corrections, ideas, 113 | or problems with the what's in here, please get in touch. -------------------------------------------------------------------------------- /draft/outline/service.markdown: -------------------------------------------------------------------------------- 1 | ^title Service 2 | ^theme Communicating 3 | ^outline 4 | 5 | ## Intent 6 | *Provide a global point of access to a service, without coupling users 7 | of the service to the concrete class that implements it.* 8 | 9 | ## Motivation 10 | Many systems in a game need to cause sounds to play. Want to provide 11 | global access to ability to play sound. 12 | 13 | Usually use a Singleton for this, but that still couples to concrete class, and also brings in an unneeded single instance limitation. 14 | 15 | Better is to separate out the class providing *access* to a service 16 | from the class *implementing* it. This way, game is only coupled to 17 | abstract service class, and not any concrete audio classes. 18 | 19 | ## The Pattern 20 | **Service Provider** class provides a globally-accessible reference to an instance of a **Service**, an interface for a set of operations. At 21 | runtime, an instance of a **Service Implementation** is registered 22 | with the provider. 23 | 24 | ## When to Use It 25 | Use it when many things will want access to a single content-independent module: audio, networking, visual effects. 26 | 27 | Very useful when there may be multiple implementations of the service. 28 | 29 | ## Keep in Mind 30 | * It's global, anything globally accessible can still cause unwanted 31 | interdependencies. Don't overuse. 32 | 33 | * Unlike Singleton, Service isn't automatically instantiated. Make 34 | sure you register a service before it's used. 35 | 36 | * Service doesn't have to be *global*. Could also limit it to a 37 | base class. For example, audio service is registered with the 38 | base Actor class, which then exposes it to all subclasses. 39 | 40 | * Have to go through v-table for calls. May not be efficient enough 41 | for services used very frequently. 42 | 43 | ## Design Decisions 44 | 45 | * What is the scope of the service (who can use it)? Global? 46 | Namespace? Class? Narrowed minimizes coupling, but can cause you 47 | to have multiple classes store reference to same service. Means 48 | you have to be careful that the service is registered everywhere 49 | it needs to be. 50 | 51 | * Who registers the service? One place in code will be coupled to 52 | both the service provider, and the concrete implementation. 53 | 54 | * What happens if service before implementation is registered? 55 | 56 | Can handle this with a **Null Service**: an implementation of the 57 | service that does nothing. Return instance of this if you try 58 | to use Service without registering implementation. Allows you to 59 | safely turn off features, such as removing audio during 60 | development. 61 | 62 | * What is the API for the service itself? This is a public interface 63 | that will be used and implemented frequently. Take care to design 64 | it well. 65 | 66 | ## Sample Code 67 | 68 | ### The Service 69 | *Show an example AudioService API, an abstract class with a couple of 70 | PlaySound() methods.* 71 | 72 | *Show an example implementation, AudioImpl (bodies of functions will 73 | not actually be implemented).* 74 | 75 | ### A Simple Provider 76 | *Show static class AudioProvider. Has methods to get and set 77 | AudioService pointer.* 78 | 79 | ### Service Unavailable 80 | *Show NullAudioImpl null service class. Show how AudioService returns 81 | a pointer to that if no other service is registered.* 82 | 83 | ### Logging Decorator 84 | Logging is useful during development, but don't want the performance 85 | hit in the shipping game. Often use #ifdefs for this, but we can also 86 | use Service and Decorator to a cleaner effect. 87 | 88 | *Show AudioLogger class that decorates AudioService by logging and 89 | then forwarding call to wrapped instance.* 90 | 91 | *Show how code registering service can conditionally wrap in logging 92 | when desired.* 93 | 94 | ## See Also 95 | *Compare to Singleton.* -------------------------------------------------------------------------------- /draft/outline/spatial-partition.markdown: -------------------------------------------------------------------------------- 1 | ^title Spatial Partition 2 | ^section Optimizing Patterns 3 | 4 | ## Intent 5 | 6 | *Quickly find objects near a certain point or area by storing them in a data 7 | structure that organizes them by location.* 8 | 9 | ## Motivation 10 | 11 | - games inhabit virtual world. that world often mimics the real world. 12 | - one facet of that is things have a position and interact in physical space. 13 | - in other words, you can be near stuff, and stuff can collide. 14 | - obvious example is physics and collision, but other stuff too. 15 | - for online games, maybe chat messages are only heard by other players near 16 | you 17 | - only render things near the player (though visibility culling is a bit 18 | different) 19 | - audio system may take into account which objects are nearby 20 | - all of this boils down to, given a location in the game world, what's nearby 21 | - we'll pick collision as example 22 | - for collision, need to see which pairs of objects are touching. 23 | - comparing each pair in whole world is n^2! 24 | - as number of objects increases, gets worse and worse 25 | - think about hash table 26 | - hash table lets you find object with key in constant time instead of n for 27 | searching the whole collection 28 | - what if we could make a hash table where the key is its position? 29 | - this is basic idea of spatial partition: organize objects in data structure 30 | based on where they are 31 | - literal hash table with positions as keys wouldn't work, though. often need 32 | to find objects *near* a position. hash tables, by design, don't handle 33 | approximate keys well. they intentionally spread objects out so that a tiny 34 | key change is a big location change. 35 | 36 | ## The Pattern 37 | 38 | - create data structure where objects are organized based on location in game 39 | world 40 | - allow efficient queries to find objects at or near a location 41 | 42 | ## When to Use It 43 | 44 | - when objects have position 45 | - doing frequent queries to find nearby objects 46 | - there are a lot of objects 47 | 48 | ## Keep in Mind 49 | 50 | - goal is generally to reduce a n^2 operation to n or a n to 1. if number of 51 | objects is actually small, may not be worth it. 52 | 53 | - if objects can move, they have to be reorganized in the data structure. 54 | there is some overhead to this (especially with hierarchical spatial 55 | partitions) so make sure benefits outweight organization 56 | 57 | - data structure has some memory overhead. as usual, trading memory for perf. 58 | 59 | ## Sample Code 60 | 61 | - lots of spatial partition data structures. area of active research. 62 | - since we just care about pattern, we'll do the simplest one: a fixed grid. 63 | - rts. lots and lots of units running around battlefield. need to see which 64 | units are near enough to engage in melee. 65 | - naive solution 66 | - for each unit 67 | - for each other unit 68 | - skip redundant comparisons 69 | - bounding box test 70 | - see if distance < minimum 71 | - naive solution is n^2. 72 | - want to have *lots* of units and game is slowing down. 73 | - introducing grid 74 | - overlay 2d grid overworld. each cell has a fixed size. 75 | - each cell stores list of units 76 | - units whose position falls within that cell are in that cell's list 77 | - finding collisions 78 | - for each unit 79 | - find its cell 80 | - for each other unit in cell 81 | - now same as before 82 | - still seems to be n^2: two nested loops 83 | - but since there are many cells, each cell will have fewer units 84 | - neighboring cells 85 | - if near cell boundary, may be touching units whose centerpoint is in 86 | neighboring cell. need to take those into account too. 87 | 88 | ## Design Decisions 89 | 90 | - which specific spatial partition to use. lots of options: grid, bsp, etc. 91 | - does it adapt automatically to the set of objects or is it fixed like a grid? 92 | - fixed is simpler 93 | - may be faster too 94 | - adaptable works better for large levels, or with user-authored content 95 | - some are optimized for objects that don't move 96 | - are objects *only* stored in spatial partition or is there a separate direct 97 | list of them too? 98 | - former uses less memory. can be slow if you need to visit all objects and 99 | most of the spatial partition is empty. 100 | - is it hierarchical or not? 101 | - hierarchical works better when objects are non-evenly distributed ("clumpy") 102 | - flat is simpler 103 | 104 | ## See Also 105 | 106 | * http://en.wikipedia.org/wiki/Space_partitioning 107 | * http://en.wikipedia.org/wiki/Grid_(spatial_index) 108 | * http://en.wikipedia.org/wiki/Kd-tree 109 | * http://en.wikipedia.org/wiki/Binary_space_partitioning 110 | * http://en.wikipedia.org/wiki/Quad_tree 111 | -------------------------------------------------------------------------------- /html/acknowledgements.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Acknowledgements · Game Programming Patterns 7 | 8 | 10 | 11 | 12 | 13 | 22 | 23 | 24 | 25 | 26 | 77 |
© 2009-2021 Robert Nystrom
78 | 79 | 80 | -------------------------------------------------------------------------------- /html/behavioral-patterns.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Behavioral Patterns · Game Programming Patterns 7 | 8 | 10 | 11 | 12 | 13 | 22 | 23 | 24 | 25 | 26 | 63 |
© 2009-2021 Robert Nystrom
64 | 65 | 66 | -------------------------------------------------------------------------------- /html/decoupling-patterns.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Decoupling Patterns · Game Programming Patterns 7 | 8 | 10 | 11 | 12 | 13 | 22 | 23 | 24 | 25 | 26 | 61 |
© 2009-2021 Robert Nystrom
62 | 63 | 64 | -------------------------------------------------------------------------------- /html/design-patterns-revisited.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Design Patterns Revisited · Game Programming Patterns 7 | 8 | 10 | 11 | 12 | 13 | 22 | 23 | 24 | 25 | 26 | 68 |
© 2009-2021 Robert Nystrom
69 | 70 | 71 | -------------------------------------------------------------------------------- /html/images/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/Thumbs.db -------------------------------------------------------------------------------- /html/images/architecture-cycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/architecture-cycle.png -------------------------------------------------------------------------------- /html/images/arrow-inherits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/arrow-inherits.png -------------------------------------------------------------------------------- /html/images/arrow-references.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/arrow-references.png -------------------------------------------------------------------------------- /html/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/background.png -------------------------------------------------------------------------------- /html/images/bytecode-ast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-ast.png -------------------------------------------------------------------------------- /html/images/bytecode-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-code.png -------------------------------------------------------------------------------- /html/images/bytecode-literal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-literal.png -------------------------------------------------------------------------------- /html/images/bytecode-numbers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-numbers.png -------------------------------------------------------------------------------- /html/images/bytecode-stack-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-stack-1.png -------------------------------------------------------------------------------- /html/images/bytecode-stack-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-stack-2.png -------------------------------------------------------------------------------- /html/images/bytecode-stack-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-stack-3.png -------------------------------------------------------------------------------- /html/images/bytecode-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/bytecode-ui.png -------------------------------------------------------------------------------- /html/images/command-buttons-one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/command-buttons-one.png -------------------------------------------------------------------------------- /html/images/command-buttons-two.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/command-buttons-two.png -------------------------------------------------------------------------------- /html/images/command-stream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/command-stream.png -------------------------------------------------------------------------------- /html/images/command-undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/command-undo.png -------------------------------------------------------------------------------- /html/images/component-uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/component-uml.png -------------------------------------------------------------------------------- /html/images/data-locality-cache-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/data-locality-cache-line.png -------------------------------------------------------------------------------- /html/images/data-locality-chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/data-locality-chart.png -------------------------------------------------------------------------------- /html/images/data-locality-component-arrays.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/data-locality-component-arrays.png -------------------------------------------------------------------------------- /html/images/data-locality-pointer-chasing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/data-locality-pointer-chasing.png -------------------------------------------------------------------------------- /html/images/data-locality-things.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/data-locality-things.png -------------------------------------------------------------------------------- /html/images/dirty-flag-multiply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dirty-flag-multiply.png -------------------------------------------------------------------------------- /html/images/dirty-flag-pirate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dirty-flag-pirate.png -------------------------------------------------------------------------------- /html/images/dirty-flag-title-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dirty-flag-title-bar.png -------------------------------------------------------------------------------- /html/images/dirty-flag-update-bad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dirty-flag-update-bad.png -------------------------------------------------------------------------------- /html/images/dirty-flag-update-good.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dirty-flag-update-good.png -------------------------------------------------------------------------------- /html/images/dogshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/dogshot.jpg -------------------------------------------------------------------------------- /html/images/double-buffer-face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/double-buffer-face.png -------------------------------------------------------------------------------- /html/images/double-buffer-slaps-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/double-buffer-slaps-1.png -------------------------------------------------------------------------------- /html/images/double-buffer-slaps-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/double-buffer-slaps-2.png -------------------------------------------------------------------------------- /html/images/double-buffer-tearing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/double-buffer-tearing.png -------------------------------------------------------------------------------- /html/images/event-queue-central.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/event-queue-central.png -------------------------------------------------------------------------------- /html/images/event-queue-crawl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/event-queue-crawl.png -------------------------------------------------------------------------------- /html/images/event-queue-loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/event-queue-loop.png -------------------------------------------------------------------------------- /html/images/event-queue-queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/event-queue-queue.png -------------------------------------------------------------------------------- /html/images/event-queue-ring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/event-queue-ring.png -------------------------------------------------------------------------------- /html/images/flyweight-tiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/flyweight-tiles.png -------------------------------------------------------------------------------- /html/images/flyweight-tree-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/flyweight-tree-model.png -------------------------------------------------------------------------------- /html/images/flyweight-trees.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/flyweight-trees.png -------------------------------------------------------------------------------- /html/images/game-loop-fixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/game-loop-fixed.png -------------------------------------------------------------------------------- /html/images/game-loop-simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/game-loop-simple.png -------------------------------------------------------------------------------- /html/images/game-loop-timeline-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/game-loop-timeline-close.png -------------------------------------------------------------------------------- /html/images/game-loop-timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/game-loop-timeline.png -------------------------------------------------------------------------------- /html/images/object-pool-heap-fragment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/object-pool-heap-fragment.png -------------------------------------------------------------------------------- /html/images/observer-linked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/observer-linked.png -------------------------------------------------------------------------------- /html/images/observer-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/observer-list.png -------------------------------------------------------------------------------- /html/images/observer-nodes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/observer-nodes.png -------------------------------------------------------------------------------- /html/images/observer-weasel-wielder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/observer-weasel-wielder.png -------------------------------------------------------------------------------- /html/images/prototype-class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-class.png -------------------------------------------------------------------------------- /html/images/prototype-delegate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-delegate.png -------------------------------------------------------------------------------- /html/images/prototype-hierarchies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-hierarchies.png -------------------------------------------------------------------------------- /html/images/prototype-object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-object.png -------------------------------------------------------------------------------- /html/images/prototype-spawner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-spawner.png -------------------------------------------------------------------------------- /html/images/prototype-weapon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/prototype-weapon.png -------------------------------------------------------------------------------- /html/images/spatial-partition-adjacent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-adjacent.png -------------------------------------------------------------------------------- /html/images/spatial-partition-battle-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-battle-line.png -------------------------------------------------------------------------------- /html/images/spatial-partition-grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-grid.png -------------------------------------------------------------------------------- /html/images/spatial-partition-linked-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-linked-list.png -------------------------------------------------------------------------------- /html/images/spatial-partition-neighbors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-neighbors.png -------------------------------------------------------------------------------- /html/images/spatial-partition-quadtree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/spatial-partition-quadtree.png -------------------------------------------------------------------------------- /html/images/state-flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/state-flowchart.png -------------------------------------------------------------------------------- /html/images/state-pushdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/state-pushdown.png -------------------------------------------------------------------------------- /html/images/type-object-breed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/type-object-breed.png -------------------------------------------------------------------------------- /html/images/type-object-subclasses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/type-object-subclasses.png -------------------------------------------------------------------------------- /html/images/update-method-remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/update-method-remove.png -------------------------------------------------------------------------------- /html/images/update-method-uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munificent/game-programming-patterns/898b1f2e1818c80d7eed5eba98e9903ec13b7771/html/images/update-method-uml.png -------------------------------------------------------------------------------- /html/optimization-patterns.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Optimization Patterns · Game Programming Patterns 7 | 8 | 10 | 11 | 12 | 13 | 22 | 23 | 24 | 25 | 26 | 65 |
© 2009-2021 Robert Nystrom
66 | 67 | 68 | -------------------------------------------------------------------------------- /html/script.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $(window).resize(refreshAsides); 3 | 4 | // Since we may not have the height correct for the images, adjust the asides 5 | // too when an image is loaded. 6 | $('img').on('load', function() { 7 | refreshAsides(); 8 | }); 9 | 10 | // On the off chance the browser supports the new font loader API, use it. 11 | if (document.fontloader) { 12 | document.fontloader.notifyWhenFontsReady(function() { 13 | refreshAsides(); 14 | }); 15 | } 16 | 17 | // Lame. Just do another refresh after a second when the font is *probably* 18 | // loaded to hack around the fact that the metrics changed a bit. 19 | window.setTimeout(refreshAsides, 200); 20 | 21 | refreshAsides(); 22 | }); 23 | 24 | function refreshAsides() { 25 | // Don't position them if they're inline. 26 | if ($(document).width() < 800) return; 27 | 28 | // Vertically position the asides next to the span they annotate. 29 | $("aside").each(function() { 30 | var aside = $(this); 31 | 32 | // Find the span the aside should be anchored next to. 33 | var name = aside.attr("name"); 34 | var span = $("span[name='" + name + "']"); 35 | if (span == null) { 36 | window.console.log("Could not find span for '" + name + "'"); 37 | return; 38 | } 39 | 40 | aside.offset({top: span.position().top - 3}); 41 | }); 42 | } -------------------------------------------------------------------------------- /html/sequencing-patterns.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sequencing Patterns · Game Programming Patterns 7 | 8 | 10 | 11 | 12 | 13 | 22 | 23 | 24 | 25 | 26 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /html/style.css.map: -------------------------------------------------------------------------------- 1 | <<<<<<< Updated upstream 2 | {"version":3,"sourceRoot":"","sources":["../asset/style.scss"],"names":[],"mappings":";AASA;EACE;;;AAGF;EACE;EACA;EACA;EAEA;EACA;;;AAGF;EACE;EAEA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EAGA;EAEA;;;AAGF;EACE;EAEA;EACA;;;AAOF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE,OA9FQ;EA+FR;EACA;EACA;EACA;;;AAGF;EACE,OAtGQ;EAuGR;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE,OAtHQ;EAuHR;EACA;EAEA;EAEA;;;AAIF;EACE;EAEA;EACA;;;AAIF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE,OAvLQ;;;AA0LV;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EAGA;;;AAGF;EACE;;;AAKF;EACE;EAGA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;;;AAKA;EACE;;AAGF;EACE;;AAIF;EACE;EACA;EACA;;;AAIJ;EACE;EACA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;;;AAKF;EACE;EACA;EACA;EACA;;;AAIF;EACE;EAEA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EAEA;;AAKF;EACE;EACA;EACA;;AAGF;EAA8B;;AAE9B;EACE;EACA;EACA;;AAIF;EAAoC;EAAY;EAAY;EAA2B;EAAiB;EAAU;EAAc;;AAChI;EAA+B;EAAc;EAAyB;EAAkB;EAAY;EAAY;EAAW;;AAC3H;EAAsC;;AACtC;EAAwC;EAAe;;AACvD;EAA8B;EAAe;EAAY;EAAY;EAAoB;EAAiB;;;AAQ5G;EACE;;;AAGF;AAAA;AAAA;EAGE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIF;EACE;EACA;EAEA;EACA;EACA;;;AAGF;EACE;EACA;;;AAIF;EAAkB,OAldR;;;AAmdV;EAAkB,OAndR;;;AAodV;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB,OA5dR;;;AA6dV;EAAkB,OA7dR;;;AAieV;EAAmB,OAjeT;;;AAoeV;EACE;IACE;;;EAGF;IACE;IACA;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;IACA;IACA;IACA;;;EAGF;IACE;IACA;IACA;IACA;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;IACA;IAGA;;;EAGF;IACE;;;EAIA;IACE;;;EAIJ;IACE;IACA;IACA;;;EAGF;IACE;;;AAKJ;EACE;IACE;;;EAGF;IACE;IACA;IACA;IAGA;IACA;IACA;IAEA;IACA;IACA;;EAEA;IACE;;;EAQF;IACE;IACA;;;EAMF;IACE;;EAGF;IACE","file":"style.css"} 3 | ======= 4 | {"version":3,"sourceRoot":"","sources":["../asset/style.scss"],"names":[],"mappings":";AASA;EACE;;;AAGF;EACE;EACA;EACA;EAEA;EACA;;;AAGF;EACE;EAEA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EAGA;EAEA;;;AAGF;EACE;EAEA;EACA;;;AAOF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE,OA5FQ;EA6FR;EACA;EACA;EACA;;;AAGF;EACE,OApGQ;EAqGR;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAKF;EACE;;AAEA;EACE;;AAGF;EACE;;;AAIJ;EACE,OAlIQ;EAmIR;EACA;EAEA;EAEA;;;AAIF;EACE;EAEA;EACA;;;AAIF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;;AAIJ;EACE;EACA,aAhLK;EAiLL;;;AAGF;EACE;;;AAGF;EACE,OA3LQ;;;AA8LV;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EAGA;;;AAGF;EACE;;;AAKF;EACE;EAGA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;;;AAQF;EACE;;;AAGF;AAAA;AAAA;EAGE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIF;EACE;EACA;EAEA;EACA;EACA;;;AAIF;EAAkB,OA9SR;;;AA+SV;EAAkB,OA/SR;;;AAgTV;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB;;;AAClB;EAAkB,OAxTR;;;AAyTV;EAAkB,OAzTR;;;AA6TV;EAAmB,OA7TT;;;AAgUV;EACE;IACE;;;EAGF;IACE;IACA;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;IACA;IACA;IACA;;;EAGF;IACE;IACA;IACA;IACA;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;;;EAGF;IACE;IACA;IAGA;;;EAGF;IACE;;;EAIA;IACE;;;EAIJ;IACE;IACA;IACA;;;EAGF;IACE;;;EAGF;IACE;;;AAKJ;EACE;IACE;;;EAGF;IACE;IACA;IACA;IAGA;IACA;IACA;IAEA;IACA;IACA;;EAEA;IACE","file":"style.css"} 5 | >>>>>>> Stashed changes 6 | -------------------------------------------------------------------------------- /note/bio.txt: -------------------------------------------------------------------------------- 1 | Robert Nystrom has programmed professionally for twenty years, about half of which is in games. During his eight years at Electronic Arts, he worked on behemoths like Madden and smaller titles like Henry Hatsworth in the Puzzling Adventure. He's shipped games on the PC, GameCube, PS2, XBox, X360, and DS, but is most proud of the tools and shared libraries he created for others to build on. He loves seeing usable, beautiful code magnify the creative ability of others. 2 | 3 | Robert lives with his wife and two daughters in Seattle where you are most likely to find him cooking for his friends and plying them with good beer. 4 | -------------------------------------------------------------------------------- /note/description.txt: -------------------------------------------------------------------------------- 1 | The biggest challenge facing many game programmers is completing their game. Most game projects fizzle out, overwhelmed by the complexity of their own code. Game Programming Patterns tackles that exact problem. Based on years of experience in shipped AAA titles, this book collects proven patterns to untangle and optimize your game, organized as independent recipes so you can pick just the patterns you need. 2 | 3 | You will learn how to write a robust game loop, how to organize your entities using components, and take advantage of the CPUs cache to improve your performance. You'll dive deep into how scripting engines encode behavior, how quadtrees and other spatial partitions optimize your engine, and how other classic design patterns can be used in games. 4 | -------------------------------------------------------------------------------- /note/dimensions.txt: -------------------------------------------------------------------------------- 1 | Here's some dimensions of popular programming books on Amazon: 2 | 3 | minecraft for dummies 5.4 x 8.6 x 0.3 inches 4 | touch develop 6 x 9 x 0.6 inches 5 | don't make me think 7 x 9.1 x 0.5 inches 6 | learning python 7.1 x 9.3 x 2.8 inches 7 | beginning c++ 7.2 x 9 x 1.1 inches 8 | node.js in action 7.4 x 9.2 x 0.8 inches 9 | unity 4 by example 7.5 x 9.2 x 1.2 inches 10 | html & css 7.9 x 9.6 x 1 inches 11 | invent your own games 8 x 10 x 0.9 inches 12 | 13 | Print on demand printers: 14 | 15 | lulu.com 16 | createspace.com 17 | lightningsource.com 18 | blurb.com (seems to target mostly photo books) 19 | bookbaby.com 20 | 21 | Based on those and looking at prices on Lulu, I think the best choice is: 22 | 23 | Product Line: Standard 24 | Binding: Perfect Bound Paperback 25 | Product Size: Crown Quarto (7.44 x 9.68 inch) 26 | Interior Color: Black & White 27 | Paper Quality: 60# 28 | Cover Finish: Gloss 29 | Cost per 280 page book: $9.80 30 | 31 | On CreateSpace: 32 | 33 | Interior Type: Black and White 34 | Trim Size: 7.5" x 9.25" 35 | Number of Pages: 280 36 | Quantity: 1 37 | Per Book: $4.21 each 38 | 39 | 40 | Margins: 41 | 42 | InDesign Classroom in a Book: 43 | Bottom: ~0.375" 44 | Outside: ~0.375" 45 | Top: ~0.5" 46 | Inside: ~0.875." 47 | 48 | SICP: 49 | Top: ~0.5" 50 | Outside: ~0.75" 51 | Bottom: ~0.75" 52 | Inside: ~1.0" 53 | -------------------------------------------------------------------------------- /note/fonts.txt: -------------------------------------------------------------------------------- 1 | Body 2 | ---- 3 | 4 | Maybe: 5 | 6 | Albertina 7 | Andron 8 | Bembo 9 | Berthold Baskerville Book 10 | Cala (Hoftype) 11 | http://www.myfonts.com/fonts/hoftype/cala/ 12 | *Very* readable. Nice minimal thickness changes should balance well with code 13 | font. Nice ligatures. Love the italics. 14 | Caslon 15 | Century Schoolbook 16 | Dante 17 | http://www.myfonts.com/fonts/mti/dante-mt/ 18 | Looks nice. Vertical "o" looks nice and modern. Italics are a bit too old 19 | style. 20 | Dolly 21 | Electra 22 | Elena 23 | http://processtypefoundry.com/fonts/elena/ 24 | Nice modern serif. A bit brushstroke-y. 25 | Fenland 26 | Girando Pro 27 | Harriet Text Regular (Okay Type) 28 | Sweet Jesus, it's heavenly. May get played out in a few years. Super 29 | fashionable right now. 30 | Lyon 31 | Mercury 32 | Sabon Next 33 | http://www.linotype.com/240/sabon.html 34 | Very readable, very lucid. I think it will pair well. A bit too boring and 35 | too similar to Times New Roman. 36 | Sina Nova 37 | http://www.fontsquirrel.com/fonts/sina-nova 38 | Very nice. Similar to Cala. 39 | Sirba 40 | http://www.type-together.com/Sirba 41 | Very beautiful. Modern, a bit informal. Would match the prose well. 42 | Tundra 43 | https://www.fontfont.com/fonts/tundra 44 | Beautiful, clean, lucid. Very strong contender. Maybe a bit too square and 45 | narrow. 46 | 47 | No: 48 | 49 | Janson - Classy, but a bit thin at points. 50 | Jenson - Beautiful font but too old style for my writing. 51 | Minion - Too 90's, overdone. 52 | Scala - Too angular. 53 | Augustin 54 | http://www.myfonts.com/fonts/ludwiguebele/augustin/ 55 | Pretty. Italics are nice. Tail on "y" and "j" are distinctive but not in a 56 | way that would fit well with the book. Might clash with the code font. 57 | Comenia 58 | OK. Has a matching sans, but no light weights. Regular weight is a bit heavy. 59 | Documenta - Too old style. 60 | Expo - Meh. Too... artsy? Feels like it would be good for a history text, but 61 | not my book. 62 | Iowan OS 63 | http://www.myfonts.com/fonts/bitstream/iowan-old-style/ 64 | Nice. A bit plain. Diamond dots over i's. 65 | Miller Text Regular - Similar to Harriet. Very pretty, but a bit too dense. 66 | Milo Serif - Not a fan. 67 | Novel 68 | http://www.myfonts.com/fonts/atlas-font-foundry/novel-std/ 69 | Clean and pretty. The italics are meh. 70 | Quadraat - "B" is WTF. Italics too angular. 71 | Stuart - Neat, but not the right character for the book. 72 | Storm Baskerville 10 - Too Victorian. 73 | Verdigris - Very beautiful, similar to Jenson and Sabon. Too old style. 74 | Yoga - Chunky. 75 | 76 | http://www.typophile.com/node/100581 77 | http://www.myfonts.com/users/pjy2ao1bxc/albums/591331/ 78 | 79 | Sans 80 | ---- 81 | 82 | News Gothic 83 | JAF Bernini Sans 84 | 85 | No: 86 | 87 | Franklin Gothic - Too heavy. 88 | Folio - Too wide. 89 | -------------------------------------------------------------------------------- /note/print workflow.txt: -------------------------------------------------------------------------------- 1 | - Export to XML 2 | - Import XML 3 | - Place it in main text flow 4 | - Untag everything and remove the story structure 5 | - Pull out asides and restyle 6 | - Change first paragraph body text styles 7 | - Set chapter title and number on first page 8 | - Update section in running footer 9 | - Clean up 10 | - Make sure there aren't bits of 14.4pt line height 11 | - Format bullet lists 12 | - Including first and last items 13 | - Make sure italics in headers are correct 14 | - Add illustrations 15 | - Make 600 dpi bitmap 16 | - Save to Print/Illustrations/... 17 | - Place 18 | - Make inline anchor 19 | - Set Position to "Above Line" with 9pts of space before and 13.5pts after 20 | - Add a caption 21 | - Position asides -------------------------------------------------------------------------------- /note/publishing.txt: -------------------------------------------------------------------------------- 1 | - Purchase 10 ISBNs 2 | Will need one for print and one for ebook, and 10 costs as much as two 3 | - Create account on Amazon Author Central 4 | Need to have book on Amazon first 5 | - Write description for book 6 | - Publisher Genever/Benning 7 | 8 | --- 9 | 10 | leanpub.com 11 | gumroad 12 | e-junkie 13 | lulu 14 | blurb 15 | https://www.softcover.io/ 16 | smashwords 17 | 18 | https://news.ycombinator.com/item?id=6052075 19 | https://news.ycombinator.com/item?id=7030112 20 | https://news.ycombinator.com/item?id=632104 21 | https://www.goodreads.com/topic/show/849429-lulu-vs-createspace 22 | http://johnplogsdon.blogspot.com/2013/04/physical-books-createspace-vs-lulu-vs.html 23 | 24 | --- 25 | 26 | My advice to anyone else considering self-publishing: 27 | * Start with Amazon KDP and CreateSpace. Both services are easy and fast. It's the quickest way to test an idea. 28 | * Be prepared to iterate quickly based on reader feedback. 29 | * Consider paying $100 or $200 for a decent cover. You can find designers lurking around the online writing communities (such as kboards.com for fiction) or hire someone on oDesk. Make sure they have experience designing book covers, which will save time and frustration. 30 | * Have someone proof your manuscript. I see lots of writers who skip this step, and suffer in the ratings and reviews as a result. 31 | * Have a cover blurb and Amazon description that grabs people. Also, make sure that readers can easily find out about you, either through the product listing page (which Amazon grabs from Amazon Author Central) or your own product website. 32 | * If you want to use other platforms, Apple's iBookstore seems most promising. It's hard to set up, though. "iTunes Producer" is a very rough piece of software. However, if you've worked on iOS apps in the past at least you will be familiar with iTunes Connect, which is used to set pricing and monitor sales. 33 | * I have sold many PDFs, but I am not sure how that would work for fiction. I started with e-junkie but switched to Gumroad (3) which has a much better interface. 34 | Marketing is tough. One thing you can do once you have a print version through CreateSpace or another service, join Goodreads (a social network for people who love to read) and set up a Goodreads Giveaway (4) (a contest for your book that Goodreads runs -- usually a few hundred people sign up, and you have to send out 10 or 20 copies to winners that GR selects). It's free to set up, but you'll have to purchase and send out copies of the book to the winners of the giveaway. The advantages of this: Readers often write reviews, which are seen by other GR members. Many other people will put the book on their "to-read" list, and some will go out and buy the book right away because they don't want to wait to see if they won a copy. 35 | Good luck! 36 | 1. http://www.digitalmediamachine.com/2012/09/do-people-judge-e... 37 | 2. http://in30minutes.com/ 38 | 3. https://gumroad.com/ 39 | 4. http://goodreads.com/giveaway 40 | 41 | http://snook.ca/archives/writing/selling-ebook-on-amazon 42 | http://apethebook.com/ 43 | 44 | --- 45 | 46 | Spent some time looking into using leanpub.com for generating the epub and mobi 47 | versions. It's pretty simple and works pretty well, if a bit slow. The landing 48 | page is nice. 49 | 50 | The biggest problem is that the output doesn't look that great. The stylsheet 51 | doesn't handle asides and large bullet points very well. They don't currently 52 | give you any way to customize this, and if I hack it myself, I can't sell it 53 | through them. 54 | 55 | Q: Does that matter? 56 | 57 | It may be simpler just to create the epub archive myself since I'm already 58 | doing much of the work for it already. 59 | 60 | 61 | --- 62 | 63 | Looked into being able to sell the epub and mobi files directly from my site. 64 | It sounds nice in theory, but I think it's not worth the trouble. Pros: 65 | 66 | - More money since a publisher won't take a cut. 67 | - People can get the book right from the site. 68 | 69 | Cons: 70 | 71 | - Technically complex to set up. 72 | - Many users don't know what to do with a raw downloaded .epub or .mobi file. 73 | Marketplaces like Kindle and iBooks handle getting it into the user's 74 | device. 75 | - May cannibalize my sales on those marketplaces. That in turn could lower the 76 | book's ranking there. 77 | 78 | All of those cons are serious, but I think the last point is the real nail in 79 | the coffin. Instead, I think I'll just have links to buy the book from a few 80 | different marketplaces. 81 | 82 | --- 83 | 84 | http://www.hxa.name/articles/content/epub-guide_hxa7241_2007.html 85 | -------------------------------------------------------------------------------- /note/references.txt: -------------------------------------------------------------------------------- 1 | http://gamadu.com/artemis/ 2 | http://html5quintus.com 3 | 4 | http://eventuallyconsistent.net/2013/08/12/messaging-as-a-programming-model-part-1/ 5 | http://research.scee.net/files/presentations/gcapaustralia09/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf 6 | 7 | angel 2d 8 | 9 | http://entity-systems.wikidot.com/ -------------------------------------------------------------------------------- /note/style.markdown: -------------------------------------------------------------------------------- 1 | ## Prose 2 | 3 | - `##` headers are title caps. 4 | - `###` subheaders are sentence caps. 5 | - "You" refers to the programmer. 6 | - The player is genderless and referred to as "they". 7 | - Ownership is expressed as "owner's thing" rather than "thing of the owner". 8 | - Contractions are fine if it helps read conversationally. 9 | - Keyboard keys have title case and are written as full words e.g. Control-Z. 10 | - Code blocks are preceded by a colon. 11 | - Bullet points may be sentence fragments but start with a cap and end with a period. 12 | - The "See Also" section consists entirely of bullet points. 13 | - Commas before conjunctions are fine. 14 | - American spelling. 15 | - References to class name, methods or any code use code format. 16 | - Italics for emphasis. 17 | - Quote block used for sentence long quotes but double quote ("") used for references what other people call something e.g. GoF call this the "subject". 18 | - Colon precedes block quote. 19 | - Punctuation appears outside quotation marks unless the punctuation is part of the quoted matter (logical punctuation). 20 | - Items in a numbered list are complete sentences with starting caps and ending period. 21 | - Use "Naïve" instead of "Naive". 22 | - Use smart quotes. 23 | - Use ` -- ` for em dashes. 24 | - Prefer present tense instead of future. 25 | - Prefer "we" over "you" or "I". 26 | - Use *O(n²)* for Big-O notation, not `O(n^2)`. 27 | - Put class name in `CodeFont` when referring to the class itself. 28 | - It's "Foo pattern", not "Foo Pattern", and the link just surrounds, "Foo". 29 | Ex: "Add a method to `Foo`." 30 | - Use normal font and lowercase when referring to objects of the class. 31 | Ex: "Create a new foo and pass it to `bar()`." 32 | - Capitalize when referring to pattern like proper noun, lowercase when 33 | referring to object implementing pattern. 34 | Ex: "Object Pool is a great pattern. An object pool will save you memory." 35 | - Design Patterns and other book titles are *italicized*. 36 | - Can end sentence before illustration with ":". 37 | - "vtable", all lowercase, no hyphen. 38 | - "on-screen" when an adjective ("the on-screen player"), "on screen" when a 39 | place ("the player is on screen"). 40 | - Don't use a numbered list unless the order matters. 41 | - Do not have a subheader immediately follow a header 42 | 43 | ## Code 44 | 45 | - Class and enum names are Pascal case. 46 | - Camel case method names and properties. 47 | - Trailing underscore for fields. That way isFoo() doesn't collide with isFoo 48 | field. 49 | - No "I" for interfaces. 50 | - Do use "virtual" for overridden methods. 51 | - Use "double" for floating point variables. 52 | - If base class has virtual methods, give it a virtual destructor. 53 | - Virtual destructor comes before other definitions. 54 | - Virtual destructor {} is on the same line as the signature. 55 | - Public before private. 56 | - Last entry in enum doesn't have comma. 57 | - Cases are vertically aligned if the code is one statement long. 58 | - Each property in an initializer list gets its own line. 59 | - The empty constructor code after initializer list gets its own line and vertically aligns with initializer list colon. 60 | - Function code and signature on the same line is fine if code is one line long. 61 | - Passing mutable references is fine. 62 | - Prefer postfix increment. 63 | 64 | ## Open Questions 65 | 66 | - Q: How are switch cases indented? 67 | - Q: How much should const be used? 68 | - Q: Why use ":" before code blocks but not illustrations? 69 | - Q: Use parentheses when referring to methods/functions? `foo` or `foo()`? 70 | - Q: `true` or true? 71 | - Q: Are "enum", "struct", "int", etc. normal words or `code font`? 72 | - Q: Hide fields behind getters (better style) or allow public fields (less 73 | boilerplate)? 74 | 75 | ## Pattern Chapter Structure 76 | 77 | Each of the pattern chapters outside of the ones in "Design Patterns Revisited" has the same structure: 78 | 79 | ### Intent 80 | 81 | This is a single italicized, imperative sentence explaining the core idea behind the pattern and the problem it solves. 82 | 83 | ### Motivation 84 | 85 | This is the most narrative part of the chapter. It walks through an example problem and builds up to how the pattern solves it. It may have subheadings, illustrations, and even some code samples. It tends to be pretty large. 86 | 87 | ### The Pattern 88 | 89 | The motivation section culminates in an explanation of the pattern and how it solves the previous problem. This one- or two-paragraph section summarizes that. 90 | 91 | It's written in present tense with the components of the pattern itself as subjects. The major pieces of the pattern are in **bold face** the first time they are introduced here. 92 | 93 | ### When to Use It 94 | 95 | Now that the reader sees how the pattern is useful for one problem, this short section describes which other problems are or are not a good fit for it. 96 | 97 | It's usually only a few paragraphs long and often ends with a bullet list. It does not have any subheadings. 98 | 99 | ### Keep in Mind 100 | 101 | This expands on the previous section and describes consequences -- mostly negative -- of using the pattern. If it's short, it will just be a few paragraphs with no subheaders. When longer, it will have a single paragraph followed by a few subheaders, one for each consequence. 102 | 103 | ### Sample Code 104 | 105 | This long section walks step by step through a full implementation of the pattern. It will have paragraphs, illustrations, subheaders, and lots of code. 106 | 107 | ### Design Decisions 108 | 109 | This shows how the pattern can vary along different axes within the solution space. It has a one paragraph intro followed be a series of design decisions. 110 | 111 | Each has a subheader in the form of a question. It may be followed by a few paragraphs explaining the question. 112 | 113 | After that, a bullet list covers some possible answers to that question. Each bullet in the list starts with a one sentence answer in **bold face**, ending with a colon. 114 | 115 | Then there is a subsequent couple of paragraphs explaining the answer. This may be omitted. 116 | 117 | Often, there will then be a *nested* bullet list with some ramifications of that decision. If so, each point will be one or more paragraphs, the first of which starts with an italicized sentence. 118 | 119 | There may be code samples or illustrations anywhere in here. 120 | 121 | Some of the decision question subsections don't follow this structure. They still have a subheader in the form of a question, but the body may just be prose or follow some other format. 122 | 123 | ### See Also 124 | 125 | The easiest section. It's just a bullet list of short paragraphs containing links to other resources. 126 | -------------------------------------------------------------------------------- /note/todo.txt: -------------------------------------------------------------------------------- 1 | - Fix prose references to hyperlinks to make them make sense 2 | - Index 3 | - Go through indexes for Intro - Singleton and use multi-page references 4 | where appropriate 5 | -------------------------------------------------------------------------------- /watch: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python3 script/format.py --watch $@ 3 | --------------------------------------------------------------------------------