Playing The Game

Given how concise and expressive the ruby language is, there are many interesting ways to code this. Let’s look at a few of them.

Throughout all of these examples, let players be an array of integers representing each position at the table. For example, in the case of 10 players: players = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] . (Apologies to those who insist all arrays must start at zero!)

In each code sample, we’ll delete elements from that array, in the order of execution. So again, using the above example of 10 players, we want a final result of players = [5] — since we know that’s the winning seat.

Turning the table

until players.size == 1

players.rotate!

players.delete_at(0)

end

By using Array#rotate! and Array#delete_at (note: we could also accomplish this with Array#shift but I feel that’s less expressive), we can solve the problem by effectively “turning the table” and always executing the player in the “first” seat.

2. Around and around

should_execute = [false, true].cycle

until players.size == 1

players .delete_if { should_execute.next }

end

Here’s an interesting one. First, using Array#cycle , we define an infinite enumerator: [false, true, false, true, false, ...] ; the next value represents whether the next player should be executed.

Then with Array#delete_if , we then loop through all the remaining players, over and over, until only one is left.

3. Tracking the offset

offset = 0

until players.size == 1

next_offset = offset + players.count % 2

players.delete_if.with_index do |player, position|

(position + offset) % 2 == 1

end

offset = next_offset

end

This version’s a bit uglier. But I’ve included it to illustrate a point:

Naively, when first tackling this problem, you may falsely assume that you can just loop through the array over and over, deleting odd-indexed positions. However, this assumption fails, for example, when there are 5 players: On the second loop, we need to delete even-indexed players from the array.

You could keep track of this odd vs even offset via a boolean variable, but the logic above is written with the intention to be “generalised” (as discussed later on) for any number of skips.

There are several possible variations of this method, using e.g. Array#select , Enumberable#each_slice or Array#values_at ; but they all suffer from this same “offset” problem.

4. Modular counting

delete_index = 1

until players.size == 1

players.delete_at(delete_index)

delete_index += 1

delete_index %= players.size

end