Saturday, September 4, 2010

The Snail in Golang

It's been some time since I last played with Golang so I decided to spend a bit of time getting to actually know golang by solving the classic snail problem, that is, to write a sequence of numbers in a square matrix of a given size n. Let's say we have a program called snail, running it with a number argument (for the square size) should look like this:

$ ./snail 5
 1  2  3  4  5 
16 17 18 19  6 
15 24 25 20  7 
14 23 22 21  8 
13 12 11 10  9 

So, the solution is straightforward: do alternating row and column sweeps, from the outside going inwards, while incrementing the value to be written into a matrix element.

But first, we have to be able to get the input size of the matrix. For that we'll use the flag and strconv packages. For the print out, we'll use the fmt package. Here's what I came up with.

package main

import (
 "fmt"
 "flag"
 "strconv"
)

func main() {
 // Determine the matrix size, n.
 var n int = 1
 flag.Parse()
 if len(flag.Args()) > 0 {
  n, _ = strconv.Atoi(flag.Arg(0))
 }
 if n < 1 {
  n = 1
 }
 
 // No need to be complicated for n=1
 if n == 1 {
  fmt.Println(n)
  return
 }

 // Create the matrix
 values := make([][]int, n)
 for i := 0; i < n; i++ {
  values[i] = make([]int, n)
 }

 // Fill the matrix with values
 avalue := 0
 for ifill := 0; ifill < n; ifill++ {
  fstart := ifill
  fend := n-ifill
  
  // upper x forward sweep
  for ixf := fstart; ixf < fend-1; ixf++ {
   avalue++
   values[fstart][ixf] = avalue
  }
  
  // right y forward sweep
  for iyf := fstart; iyf < fend-1; iyf++ {
   avalue++
   values[iyf][fend-1] = avalue
  }
  
  // lower x backward sweep
  for ixb := fend-1; ixb > fstart; ixb-- {
   avalue++
   values[fend-1][ixb] = avalue
  }
  
  // left y backward sweep
  for iyb := fend-1; iyb > fstart; iyb-- {
   avalue++
   values[iyb][fstart] = avalue
  }
 }
 
 // Take care of the last value for odd n
 if avalue < n*n {
  avalue++
  values[n/2][n/2] = avalue
 }
 
 // Ensure the same spacing for each element
 places := int(n*n/10)
 entry_width := 1
 for places > 0 {
  entry_width++
  places /= 10
 }
 format := fmt.Sprintf("%%%dd ", entry_width)
 
 // Print out the snail
 for i := 0; i < n; i++ {
  for j := 0; j < n ; j++ {
   fmt.Printf(format, values[i][j])
  }
  fmt.Println("")
 }
 
}

What about you? Have you tried solving this problem in golang? How would you do it better?