In fact, it’s far from dead. From the last time (56 days ago) I have pushed 45 commits. Considering the summer season, long AFKs, and a crazy time for me (for private reasons), I call it an okay-ish result.

It’s far from dead because it’s been just now that I feel more-or-less comfortable with the shape of the code.

Not dead yet, but it was close

Let’s be clear. The problem I decided to tackle in the project (simulating american football games) is stupidly complex. The rules of this sport are complex enough – after years of watching it I still have many doubts 😅. Now think about implementing the rules, plus simulating the game outcome, plus doing it all in a TDD manner (“don’t predesign”).

Even though I decided to simplify the early stage like crazy, the rest was still kinda overwhelming. Hopefully, the overwhelming is gone.

Walking skeleton

From the beginning, I was aiming for a walking skeleton. That is, for something that actually generates a game from start to beginning. Even if the game is generated under heavy assumptions. All the rest is either non-existing or stupidly simplified:

  • I assumed that teams can perform one of two actions: kick-off or run with their QB. And they don’t even choose which one they perform – it’s predecided by game rules.
  • A game consists of only one quarter
  • Each action takes the same amount of time
  • There are no season schedules, divisions, or teams yet 
  • Etc.

Implementation from the air

A game turned out to be a collection of actions, which in turn are collections of phases. Each particular phase is going to be eventually generated by players’ skills and users’ decisions on the pitch, but for now, they are mostly hardcoded.

To generate such a hierarchical structure, I implemented generators in generators. Higher-level generators control the flow and order of what happens next. Lower-level generators, well, generate phases. Eventually, lower-lever generators will take into account players’ positions, skills, and a whole lot of other conditions.

Without pre-designing, I couldn’t get any happy with temporary codebases. Now it’s close, at least I don’t feel too blocked from adding small changes. Adding complexity to existing generators or writing new ones should not disrupt too much of the existing flow. Skeleton is walking, now needs some muscles and, skin little by little.

Elegant objects

To get myself a little bit out of my comfort zone, I decide to use elegant objects.

At this stage, the most interesting piece of implementation is generating numbers from a range of possibilities (eg. for a player of a given skill).

Simplified test and implementation:

spec/kickoff_generator_spec.rb

describe "when random generator provided" do
  let(:return_event_generator) { RandomReturnPhaseGenerator.new } 

  it "generates kickoff off with random data" do
      expect(kickoff).to eq(Action.new(
         (...)
         ReturnPhase.new(returner: returner, yards_diff: YardsFromRange.new(-50, -30), next_phase: :tackle, time_in_seconds: 15),
        (...)
  end
end

lib/football_manager/yards.rb

class YardsFromRange
  def initialize(first, last, number = -1)
    @first, @last, @number = first, last, number
  end

  attr_reader :number, :first, :last

  def ==(o)
    self.class == o.class && self.first == o.first && self.last == o.last
  end
end

Note that tests are not interested at all in the exact number that has been randomized. As long as it’s a class with correct range arguments, it’s assumed to be generated correctly from between the range.