A different take on Duff’s Device

This year I set about learning a new programming language – Go (aka GoLang). I set about expanding my horizons away from my bread and butter language Java, and look at how other languages achieve things. Go comes across as very much a C with a fresh coat of paint. There’s quite a few vestiges of C that have been given a makeover.

Structs can now act like semi OO classes by bolting on functional receivers, which are methods that act on structs. Structs are always purely data and can exist just as data in their own right.

One thing I’m not quite keen on is the lack of ternary operator. It’s only used here and there but it is sorely missed when a concise piece of code (perhaps in a string output) has to now make way for an ugly if statement.

I’m also keen on their revamped use of the switch statement which takes the commonsense approach of making each individual case label an ‘enclosed’ piece of code by default. Contrasted with any C-ish type language where case statements fallt hrough automatically, Go makes the wise choice to switch up the semantics. The un-usual case requires an explicit “fallthru” statement.

This leads me to some code I wrote recently. A monopoly simulator. It’s not graphical, but rather a game played by 6 AI bots. All aspects of the game were programmed in, including chance, community chest, a very simplistic swapping strategy, purchasing houses, mortgages etc.

Lending back to the switch statement, one cool thing I implemented is a different take on Duff’s Device. Duff’s device was written by a guy at ILM to allow data that is not in boundaries of 8 bytes to be read by coding the remainder by using switch statement fallthrough.

In my case, I had 6 players in an array. Over time some of these players will become bankrupt and therefore cease to become active players. I needed a way to move to the next active player and jump over any “gaps” of inactive players.

The array can be seen more like a linkedlist as once the last element is reached, the code will wrap back to the beginning if necessary (e.g. position 5 +1 resets back to position 0)

If there are n players and x inactive players (where x < n) then the number of ‘jumps’ over the array will be at most x. But the usual case is that the next player will not require x jumps over the array. If for example there are 6 players and 3 players out and we had the following

0-Active; 1- Active; 2-Out, 3-Out,4-Active; 5; Active

Then 2 jumps is all that is needed in this particular case, but 4 would be the absolute most – and only in the specific case where all inactive players were spaced consecutively, say for instance players 2, 3, 4 are all out.

Here is the code that uses fallthrough to achieve this. It’s one of the rare times I’ve used switch statement fallthrough as a feature to solve a problem!

// true: if game has been won by a player
func (gs *GameState) NextPlayer() bool {
  //gs.CurrentPlayer = &gs.AllPlayers[(gs.CurrentPlayer.PlayerNumber+1)%totalPlayersPlaying]
  var countActive int
  for _, j := range gs.AllPlayers {
    if j.Active == true {
      countActive++
    }
  }

  switch countActive {
  case 1:
    gs.CurrentPlayer = &gs.AllPlayers[(gs.CurrentPlayer.PlayerNumber+1)%TotalPlayersPlaying]
    if gs.CurrentPlayer.Active == true {
      break
    }
    fallthrough
  case 2:
    gs.CurrentPlayer = &gs.AllPlayers[(gs.CurrentPlayer.PlayerNumber+1)%TotalPlayersPlaying]
    if gs.CurrentPlayer.Active == true {
      break
    }
    fallthrough
  case 3:
    gs.CurrentPlayer = &gs.AllPlayers[(gs.CurrentPlayer.PlayerNumber+1)%TotalPlayersPlaying]
    if gs.CurrentPlayer.Active == true {
      break
    }
    fallthrough
  case 4:
    gs.CurrentPlayer = &gs.AllPlayers[(gs.CurrentPlayer.PlayerNumber+1)%TotalPlayersPlaying]
    if gs.CurrentPlayer.Active == true {
      break
    }
    fallthrough
  case 5:
    gs.CurrentPlayer = &gs.AllPlayers[(gs.CurrentPlayer.PlayerNumber+1)%TotalPlayersPlaying]
    if gs.CurrentPlayer.Active == true {
      break
    }
    fallthrough
  default:
    gs.CurrentPlayer = &gs.AllPlayers[(gs.CurrentPlayer.PlayerNumber+1)%TotalPlayersPlaying]
    if gs.CurrentPlayer.Active == true {
      break
    }
  }
  fmt.Println(gs.CurrentPlayer.Name, "is now up")

 

Leave a Reply

Your email address will not be published. Required fields are marked *