Bowling Game Kata in Ruby 12

Posted by Brett Schuchert Thu, 01 Oct 2009 18:37:00 GMT

I have not really used Ruby much. I’ve written a few tutorials, messed around with RSpec and Test::Unit and even Rails a bit, but I really don’t know Ruby that well. I get Ruby (the MOP, instances, blocks, open classes, ...) but there’s a difference between understanding that stuff and using it day-to-day.

Last night we had a Dojo in Oklahoma City and I wanted to get refreshed with RSpec, so I decided to jump in and do the bowling game kata. I did not follow uncle bob’s lead exactly. For one, I went ahead and stored two throws for each frame. While what he ends up with is a bit shorter, it bothers me a little bit. I’ve also seen people surprised by how bob stores his scores, so in a sense it violates the law of least astonishment.

That’s neither here nor there, I got it working just fine – though handling strikes was a bit more difficult because I decided to store two rolls instead of one (so clearly, there’s no best answer, just ones that suck for different reasons).

After my first go around, I had a single spec with examples all under a single describe (what’s that term they’d use for what the describe expression creates?). I added several examples for partial scores, to make sure I was handling incomplete games correctly. I restructured those a bit and tried to make the names a bit more clear, not sure if I was successful.

In my original version I started with a frame in the score method as a local variable, but it quickly got converted to an index, and the index was mostly passed around after that. The approach was very c-esque. I didn’t like that index all over the place, so I tried to remove it by refactoring. It took several false starts before I bit the bullet and simply duplicated each of the methods, one at a time, using parallel development. The old version using an index, the new one use a 1-based frame number. After I got that working with frames, I removed most of the methods using an index, except for a few.

What follows is the spec file and the ruby class. If you read the names of some of the examples, you might think I used to bowl in league, I did. My average was a paltry 158, my best game ever a 248. Best split I ever picked up? 4, 6, 7, 10.

Comments welcome.

bowling_score_card_spec.rb

require 'bowling_score_card'

describe BowlingScoreCard do
    before(:each) do
        @bowling_game_scorer = BowlingScoreCard.new
    end

    def roll value
        @bowling_game_scorer.roll value
    end

    def roll_many count, value
        count.times { roll value }
    end

    def score_should_be value
        @bowling_game_scorer.score.should == value
    end

    it 'should score 0' do
        score_should_be 0
    end

    describe 'Scores for Complete Games' do
        it 'should score 0 for an all gutter game' do
            roll_many 20, 0
            score_should_be 0
        end

        it 'should show 20 for an all 1 game' do
            roll_many 20, 1
            score_should_be 20
        end

        it 'should score game with single spare correctly' do
            roll_many 3, 5
            roll_many 17, 0
            score_should_be 20
        end

        it 'should score game with single strike correctly' do
            roll 10
            roll 5
            roll 2
            roll_many 17, 0
            score_should_be 24
        end

        it 'should score a dutch-200, spare-strike, correclty' do
            10.times do
                roll_many 2, 5
                roll 10
            end
            roll_many 2, 5

            score_should_be 200
        end

        it 'should score a dutch-200, strike-spare, correctly' do
            10.times do
                roll 10
                roll_many 2, 5
            end
            roll 10
            score_should_be 200
        end

        it "should score all 5's game as 150" do
            roll_many 21, 5
            score_should_be 150
        end

        it 'should score a perfect game correctly' do
            roll_many 12, 10
            score_should_be 300
        end

        it 'should not count a 0, 10 roll as a strike' do
            roll 0
            roll 10
            roll_many 18, 1
            score_should_be 29
        end

    end

    describe 'Scoring for open games' do
        it 'should score just an open frame' do
            roll 4
            roll 3
            score_should_be 7
        end

        it 'should score just a spare' do
            roll_many 2, 5
            score_should_be 10
        end

        it 'should score partial game with spare and following frame only' do
            roll_many 3, 5
            score_should_be 20
        end

        it 'should score an opening turkey correctly' do
            roll_many 3, 10
            score_should_be 60
        end
    end

    describe 'Scoring open game starting with a srike' do
        before(:each) do
            roll 10
        end
        it 'should score partial game with only strike' do
            score_should_be 10
        end

        it 'should score partial game with strike and half-open frame' do
            roll 4
            score_should_be 18
        end

        it 'should score partial game with strike and open frame' do
            roll 3
            roll 6
            score_should_be 28
        end

        it 'should score partial game with strike and spare' do
            roll 3
            roll 7
            score_should_be 30
        end
    end

    describe 'Open game starting with two Strikes' do
        before(:each) do
            roll 10
            roll 10
        end

        it 'should have a score of 30' do
            score_should_be 30
        end

        it 'should score correctly with following non-mark' do
            roll 4
            score_should_be 42
        end

        it 'should score correclty with third frame open' do
            roll 4
            roll 3
            score_should_be 48
        end
    end
end

bowling_score_card.rb

class BowlingScoreCard
    def initialize
        @rolls = []
    end

    def roll value
        @rolls << value
        @rolls << 0 if first_throw_is_strike?
    end

    def score
        (1..10).inject(0) { |score, frame| score += score_for frame }
    end

    def score_for frame
        return strike_score_for frame if is_strike_at? frame
        return spare_score_for frame if is_spare_at? frame
        open_score_for frame
    end

    def first_throw_is_strike?
        is_first_throw_in_frame? && @rolls.last == 10
    end

    def is_first_throw_in_frame?
        @rolls.length.odd?
    end

    def open_score_for frame
        first_throw_for(frame) + second_throw_for(frame);
    end

    def spare_score_for frame
        open_score_for(frame) + first_throw_for(next_frame(frame))
    end

    def strike_score_for frame
        score = open_score_for(frame) + open_score_for(next_frame(frame))
        if is_strike_at? next_frame(frame)
            score += first_throw_for(next_frame(next_frame(frame)))
        end
        score
    end

    def next_frame frame
        frame + 1
    end

    def is_spare_at? frame
        (open_score_for frame) == 10 && is_strike_at?(frame) == false
    end

    def is_strike_at? frame
        first_throw_for(frame) == 10
    end

    def first_throw_for frame
        score_at_throw(index_for(frame))
    end

    def second_throw_for frame
        score_at_throw(index_for(frame) + 1)
    end

    def index_for frame
        (frame - 1) * 2
    end

    def score_at_throw index
        @rolls.length > index ? @rolls[index] : 0
    end
end

Comments

Leave a response

  1. Avatar
    David Chelimsky about 1 hour later:

    describe “something” {} => ExampleGroup

  2. Avatar
    GBGames about 2 hours later:

    I don’t know Ruby, and therefore, RSpec, but now I know what the “it” keyword is. B-)

    My only concern is that the word “kata” is used when there is no form being followed. Uncle Bob’s Bowling kata is specifically in Java. You are trying to follow along with the spirit, but as you are not using the same language (the same form) and especially because you diverged in approach, it’s no longer a kata, right?

    It’s an exercise. It’s good for developing your skill as a programmer, but until there is a form you follow precisely and accurately, calling it a kata seems like a misnomer to me. I know in programming circles that there are plenty of things called katas that aren’t what I say they should be, but is this just something to accept, or should there be a push for katas to be things that a programmer should be able to follow step-by-step?

  3. Avatar
    Brett L. Schuchert about 3 hours later:

    Thanks David, too damn obvious for my knuckle head!

    GB, as for whether or not it is a kata, I think it is. I did practice it. I did work with many of the same tests that bob used. I took advantage of some of the tricks I’ve picked up from the bdd community (in this example, only a few).

    That I did not go with the same solution, I think, is good.

    In fact, I started with the same solution, then I tried changing it around. Then when I thought I could score complete and incomplete games correctly, I spent time looking at different ways to write things. I messed around with it.

    Kata is defined as “choreographed patterns of movements practiced either solo…” (according to wikipedia). Given that, I did use the bowling game example to practice using RSpec. I’ve done the bowling kata as bob does it in Java (and C# and I think I might have done it once in C++).

    So now the questions is this, to what, precisely, does the word kata apply? It is the practice of writing a test, getting it to pass, refactoring, or is the particular problem?

    I see your take on the word. I don’t hold the same definition. It’s quite possible my definition is wrong, but I’m going to stick to it until I’m convinced otherwise. FWIW, I’ve practiced forms in martial arts. In that case, the sequence of steps was the practice. So I grok the idea, I’m just not sure how direct I want to interpret the metaphor.

  4. Avatar
    David Vrensk 3 days later:

    Brett, I’m confused by the example “it should score a dutch-200, strike-spare, correctly”. It looks like you’re rolling 31 times (10*(2+1)+1). I’m not a bowler, but it looks to me like you are playing 21 frames which surely must be too much?

    Regarding the use of the word “kata”, I think it is correct. A programming kata is not the exakt same thing as a martial arts kata. In the latter, the point is to do the exact same thing every time, with total precision and focus. Ideally, if you’d superimpose two video recordings of a master performing a kata on two different days, you’d never know that there were two recordings. If we did a programming kata the same way, we’d strive to end up with the exact same code written in the exact same order and pace. I don’t think that would be useful (but I’m willing to be proven wrong).

    Instead, the programming kata (in my mind) borrows the purpose and confinements (if that’s the best word) from the martial arts kata. The purpose is to hone and perfect our basic moves (creating test cases/specs, doing the red-green-refactor dance, doing the simplest thing that works, etc). The confinements, as I see it, is that you should be able to perform the kata without external references. If you need to look up commands or regexp syntax or anything that a programmer at some level knows about the language, the kata is above your current level. And if you need to look up the rules for bowling scoring, you have not prepared for the kata. A kata is not a time to learn new things, but to practise and hone what you know.

  5. Avatar
    Philip Schwarz 3 days later:

    @Brett,

    thanks to your post I finally got round to trying a BDD tool.

    I took your specification, turned Ruby into Groovy, trivially turned RSpec syntax into easyb syntax, and then run it against the Java code I wrote some time ago for a Bowling Kata: the only behaviours that failed where the two dutch-200 ones, which I need to investigate, possibly in the light of what David Vrensk said in the previous comment.

    Here is the translated specification:

    def roll(int pins){ game.roll(pins) } 
    def roll_many(int pins, int rolls){ rolls.times{ game.roll(pins) } }
    def score_should_be(int score) { game.score().shouldBe(score) }
    
    before "Scores for Complete Games", { game = new Game() }
    
    it "should score 0 for an all gutter game",
    {
        roll_many(0,20)
        score_should_be 0
    }
    
    it "should show 20 for an all 1 game",
    {
        roll_many(1,20)
        score_should_be 20
    }
    
    it "should score game with single spare correctly",
    {
        roll_many(5,3)
        roll_many(0,17)
        score_should_be 20
    }
    
    it "should score game with single strike correctly",
    {
        roll 10
        roll 5
        roll 2
        roll_many(0, 17) 
        score_should_be 24   
    }
    
    it "should score a dutch-200, spare-strike, correctly",
    {
        [1..10].each{
          roll_many(5, 2)
          roll 10
        }
        roll_many(5, 2)
        score_should_be 200
    }
    
    it "should score a dutch-200, strike-spare, correctly",
    {
      [1..10].each
      {
        roll 10
        roll_many(5,2)
      }
      roll 10
      score_should_be 200
    }
    
    it "should score all 5's game as 150",
    {
      roll_many(5,21)
      score_should_be 150
    }
    
    it "should score a perfect game correctly",
    {
      roll_many(10,12)
      score_should_be 300
    }
    
    it "should not count a 0, 10 roll as a strike",
    {
      roll 0
      roll 10
      roll_many(1,18)
      score_should_be 29
     }
    
    before "Scoring for open games", { game = new Game() }
    
    it "should score just an open frame",
    {
      roll 4
      roll 3
      score_should_be 7
    }
    
    it "should score just a spare",
    {
      roll_many(5,2)
      score_should_be 10
    }
    
    it "should score partial game with spare and following frame only",
    {
      roll_many(5,3)
      score_should_be 20
    }
    
    it "should score an opening turkey correctly", 
    {
      roll_many(10,3)
      score_should_be 60
    }
    
    before "Scoring open game starting with a strike",
    {
      game = new Game()
      roll 10
    }
    
    it "should score partial game with only strike",
    {
      score_should_be 10
    }
    
    it "should score partial game with strike and half-open frame", 
    {
      roll 4
      score_should_be 18
    }
    
    it "should score partial game with strike and open frame",
    {
      roll 3
      roll 6
      score_should_be 28
    }
    
    it "should score partial game with strike and spare",
    { 
      roll 3
      roll 7
      score_should_be 30
    }      
    
    before "Open game starting with two Strikes",
    {
      game = new Game()
      roll 10
      roll 10
    }
    
    it "should have a score of 30",
    {
      score_should_be 30  
    }
    
    it "should score correctly with following non-mark",
    {
      roll 4
      score_should_be 42
    }
    
    it "should score correclty with third frame open",
    {
      roll 4
      roll 3
      score_should_be 48  
    }
    
  6. Avatar
    Brett L. Schuchert 4 days later:

    David Vrensk wrote:

    Brett, I’m confused by the example …

    Do’h! David, you are correct, I am rolling too many frames. It works because of the score method (and the fact that alternating spares or strikes will result in 200 and both approaches fill up the available score slots):
      def score
            (1..10).inject(0){|score,frame|score+=score_for frame}
        end
    

    I only score 10 frames, so the extra “crap” is ignored. In both of my dutch-200 games I should have 5.times …, not 10.

    I had not yet added negative cases:
    • Rolling over 10
    • Rolling over 10 in a single frame
    • Rolling too many frames
    • ...

    If I had added those (and I thought about it but didn’t do it), I would have caught those errors and never posted the 10.times.

    I’ll leave the errors in the blog, but I’ll fix my code by adding code to validate the input, those tests will fail, then I’ll fix the tests.

    Great example of why having a navigator is good.

  7. Avatar
    Brett L. Schuchert 4 days later:

    Philip Schwarz wrote:

    ...two dutch-200 ones, which I need to investigate, possibly in the light of what…
    Yep, as David mentioned, the examples are wrong. See post just above this one.

    And cool seeing easyb. Thanks for showing us that.

  8. Avatar
    Brett L. Schuchert 4 days later:
    Whew! That took a bit more than I expected. First, there was another error in one of the examples:
          it 'should score game with single strike correctly' do
                roll 10
                roll 5
                roll 2
                roll_many 17, 0
                score_should_be 24
            end

    Rolled one too many (17—> 16). Makes sense I supose.

    Next, I added several more negative examples than expected:
        describe '10th frame number of rolls allowed' do
            before(:each) do
                roll_many 18,0
            end
    
            it 'should fail with open 10th and additional roll' do
                roll_many 2, 0
                lambda { roll 0 }.should raise_error(IndexError)
            end
    
            it 'should fail with spare 10th frame and 2 additional rolls' do
                roll_many 2, 5
                roll 5
                lambda { roll 0 }.should raise_error(IndexError)
            end
    
            it 'should fail with strike, spare and 1 additional roll' do
                roll 10
                roll_many 2, 5
                lambda { roll 0 }.should raise_error(IndexError)
            end
    
            it 'should fail with strike, strike and 2 additional rolls' do
                roll 10
                roll 10
                roll 5
                lambda { roll 0 }.should raise_error(IndexError)
            end
        end
    
        describe 'Too many pins in a frame' do
            it 'should not allow more than 10 on a single roll' do
                lambda { roll 11 }.should raise_error(ArgumentError)
            end
    
            it 'should not allow more than 10 in two rolls' do
                roll 6
                lambda { roll 5 }.should raise_error(ArgumentError)
            end
        end
    Ultimately, I ended up with a bit of changed code (and one ugly method:
        def roll value
            verify value
            record value
        end
    
        def verify value
            raise IndexError, 'Too many rolls in game' if !should_allow_roll?
            raise ArgumentError, 'Too many pins rolled' if too_many_pins? value
        end
    
        def record value
            @rolls << value
            @rolls << 0 if first_throw_is_strike?
        end
    
        def too_many_pins? value
            (open_score_for current_frame) + value > 10
        end
    
        def current_frame
            (@rolls.length / 2) + 1
        end
    
        def should_allow_roll?
            max = 20
            max = 21 if is_spare_at? 10
            max = 22 if is_strike_at? 10
            max = 23 if is_strike_at? 11
            @rolls.length < max
        end

    As you can probably guess, the should_allow_roll? method is a bit cryptic. In addition to it being long and full of magic numbers, it also has the mythical 11th frame, which is a side-effect of not handling the 10th frame as special.

    On the other hand, the score card hides all of this, so before I say this is truly bad, I need to go back to uncle bob’s solution and add in the same negative examples and see how well it grows.

    Here is the full source of the results:

    bowling_score_card_spec.rb
    require 'spec_helper'
    require 'bowling_score_card'
    
    describe BowlingScoreCard do
        before(:each) do
            @bowling_game_scorer = BowlingScoreCard.new
        end
    
        def roll value
            @bowling_game_scorer.roll value
        end
    
        def roll_many count, value
            count.times { roll value }
        end
    
        def score_should_be value
            @bowling_game_scorer.score.should == value
        end
    
        it 'should score 0' do
            score_should_be 0
        end
    
        describe 'Scores for Complete Games' do
            it 'should score 0 for an all gutter game' do
                roll_many 20, 0
                score_should_be 0
            end
    
            it 'should show 20 for an all 1 game' do
                roll_many 20, 1
                score_should_be 20
            end
    
            it 'should score game with single spare correctly' do
                roll_many 3, 5
                roll_many 17, 0
                score_should_be 20
            end
    
            it 'should score game with single strike correctly' do
                roll 10
                roll 5
                roll 2
                roll_many 16, 0
                score_should_be 24
            end
    
            it 'should score a dutch-200, spare-strike, correclty' do
                5.times do
                    roll_many 2, 5
                    roll 10
                end
                roll_many 2, 5
    
                score_should_be 200
            end
    
            it 'should score a dutch-200, strike-spare, correctly' do
                5.times do
                    roll 10
                    roll_many 2, 5
                end
                roll 10
                score_should_be 200
            end
    
            it "should score all 5's game as 150" do
                roll_many 21, 5
                score_should_be 150
            end
    
            it 'should score a perfect game correctly' do
                roll_many 12, 10
                score_should_be 300
            end
    
            it 'should not count a 0, 10 roll as a strike' do
                roll 0
                roll 10
                roll_many 18, 1
                score_should_be 29
            end
        end
    
        describe 'Scoring for open games' do
            it 'should score just an open frame' do
                roll 4
                roll 3
                score_should_be 7
            end
    
            it 'should score just a spare' do
                roll_many 2, 5
                score_should_be 10
            end
    
            it 'should score partial game with spare and following frame only' do
                roll_many 3, 5
                score_should_be 20
            end
    
            it 'should score an opening turkey correctly' do
                roll_many 3, 10
                score_should_be 60
            end
        end
    
        describe 'Scoring open game starting with a srike' do
            before(:each) do
                roll 10
            end
            it 'should score partial game with only strike' do
                score_should_be 10
            end
    
            it 'should score partial game with strike and half-open frame' do
                roll 4
                score_should_be 18
            end
    
            it 'should score partial game with strike and open frame' do
                roll 3
                roll 6
                score_should_be 28
            end
    
            it 'should score partial game with strike and spare' do
                roll 3
                roll 7
                score_should_be 30
            end
        end
    
        describe 'Open game starting with two Strikes' do
            before(:each) do
                roll 10
                roll 10
            end
    
            it 'should have a score of 30' do
                score_should_be 30
            end
    
            it 'should score correctly with following non-mark' do
                roll 4
                score_should_be 42
            end
    
            it 'should score correclty with third frame open' do
                roll 4
                roll 3
                score_should_be 48
            end
        end
    
        describe '10th frame number of rolls allowed' do
            before(:each) do
                roll_many 18,0
            end
    
            it 'should fail with open 10th and additional roll' do
                roll_many 2, 0
                lambda { roll 0 }.should raise_error(IndexError)
            end
    
            it 'should fail with spare 10th frame and 2 additional rolls' do
                roll_many 2, 5
                roll 5
                lambda { roll 0 }.should raise_error(IndexError)
            end
    
            it 'should fail with strike, spare and 1 additional roll' do
                roll 10
                roll_many 2, 5
                lambda { roll 0 }.should raise_error(IndexError)
            end
    
            it 'should fail with strike, strike and 2 additional rolls' do
                roll 10
                roll 10
                roll 5
                lambda { roll 0 }.should raise_error(IndexError)
            end
        end
    
        describe 'Too many pins in a frame' do
            it 'should not allow more than 10 on a single roll' do
                lambda { roll 11 }.should raise_error(ArgumentError)
            end
    
            it 'should not allow more than 10 in two rolls' do
                roll 6
                lambda { roll 5 }.should raise_error(ArgumentError)
            end
        end
    end

    bowling_score_card.rb

    class BowlingScoreCard
        def initialize
            @rolls = []
        end
    
        def roll value
            verify value
            record value
        end
    
        def verify value
            raise IndexError, 'Too many rolls in game' if !should_allow_roll?
            raise ArgumentError, 'Too many pins rolled' if too_many_pins? value
        end
    
        def record value
            @rolls << value
            @rolls << 0 if first_throw_is_strike?
        end
    
        def too_many_pins? value
            (open_score_for current_frame) + value > 10
        end
    
        def current_frame
            (@rolls.length / 2) + 1
        end
    
        def should_allow_roll?
            max = 20
            max = 21 if is_spare_at? 10
            max = 22 if is_strike_at? 10
            max = 23 if is_strike_at? 11
            @rolls.length < max
        end
    
        def score
            (1..10).inject(0) { |score, frame| score += score_for frame }
        end
    
        def score_for frame
            return strike_score_for frame if is_strike_at? frame
            return spare_score_for frame if is_spare_at? frame
            open_score_for frame
        end
    
        def first_throw_is_strike?
            is_first_throw_in_frame? && @rolls.last == 10
        end
    
        def is_first_throw_in_frame?
            @rolls.length.odd?
        end
    
        def open_score_for frame
            first_throw_for(frame) + second_throw_for(frame);
        end
    
        def spare_score_for frame
            open_score_for(frame) + first_throw_for(next_frame(frame))
        end
    
        def strike_score_for frame
            score = open_score_for(frame) + open_score_for(next_frame(frame))
            if is_strike_at? next_frame(frame)
                score += first_throw_for(next_frame(next_frame(frame)))
            end
            score
        end
    
        def next_frame frame
            frame + 1
        end
    
        def is_open_at? frame
            (open_score_for frame) < 10
        end
    
        def is_spare_at? frame
            (open_score_for frame) == 10 && is_strike_at?(frame) == false
        end
    
        def is_strike_at? frame
            first_throw_for(frame) == 10
        end
    
        def index_for frame
            (frame - 1) * 2
        end
    
        def first_throw_for frame
            score_at_throw(index_for(frame))
        end
    
        def second_throw_for frame
            score_at_throw(index_for(frame) + 1)
        end
    
        def score_at_throw index
            @rolls.length > index ? @rolls[index] : 0
        end
    end

    And finally, here’s the output from running spec at the command line (minus the coloring):
    BowlingScoreCard
    - should score 0
    
    BowlingScoreCard Scores for Complete Games
    - should score 0 for an all gutter game
    - should show 20 for an all 1 game
    - should score game with single spare correctly
    - should score game with single strike correctly
    - should score a dutch-200, spare-strike, correclty
    - should score a dutch-200, strike-spare, correctly
    - should score all 5's game as 150
    - should score a perfect game correctly
    - should not count a 0, 10 roll as a strike
    
    BowlingScoreCard Scoring for open games
    - should score just an open frame
    - should score just a spare
    - should score partial game with spare and following frame only
    - should score an opening turkey correctly
    
    BowlingScoreCard Scoring open game starting with a srike
    - should score partial game with only strike
    - should score partial game with strike and half-open frame
    - should score partial game with strike and open frame
    - should score partial game with strike and spare
    
    BowlingScoreCard Open game starting with two Strikes
    - should have a score of 30
    - should score correctly with following non-mark
    - should score correclty with third frame open
    
    BowlingScoreCard 10th frame number of rolls allowed
    - should fail with open 10th and additional roll
    - should fail with spare 10th frame and 2 additional rolls
    - should fail with strike, spare and 1 additional roll
    - should fail with strike, strike and 2 additional rolls
    
    BowlingScoreCard Too many pins in a frame
    - should not allow more than 10 on a single roll
    - should not allow more than 10 in two rolls
    
    Finished in 0.125306 seconds
    
    27 examples, 0 failures
    
  9. Avatar
    Games 8 months later:

    It works great. Thanks

  10. Avatar
    Games 8 months later:

    Works great

  11. Avatar
    Game Fair 9 months later:

    The access was actual c-esque. I didn’t like that basis all over the place, so I approved to abolish it by refactoring. It took several apocryphal starts afore I bit the ammo and artlessly bifold anniversary of the methods, one at a time, application alongside development. The old adaptation application an index, the new one use a 1-based anatomy number. Thank you so much for sharing details about game.

  12. Avatar
    Games Collection 10 months later:

    I started with a anatomy in the account adjustment as a bounded variable, but it bound got adapted to an index, and the basis was mostly anesthetized about afterward that. The access was actual c-esque. I didn’t like that basis all over the place, so I approved to abolish it by re-factoring.

Comments