"Exercise: OcrNumbers"

ocrNumbers: rows
"Given a 3 x 4 grid of pipes, underscores, and spaces, determine which number is represented, or whether it is garbled.
    1. To begin with, convert a simple binary font to a string containing 0 or 1.
        The binary font uses pipes and underscores, four rows high and three columns wide.
        If the input is the correct size, but not recognizable, your program should return '?'
        If the input is the incorrect size, your program should throw an error.
    2. Update your program to recognize multi-character binary strings, replacing garbled numbers with ?
    3. Update your program to recognize all numbers 0 through 9, both individually and as part of a larger string.
    4. Update your program to handle multiple numbers, one per line. When converting several lines, join the lines with commas."
"(self new ocrNumbers: #(' _ ' '| |' '|_|' '   ')) >>> '0'"
"(self new ocrNumbers: #('   ' '  |' '  |' '   ')) >>> '1'"
"(self new ocrNumbers: #(' _ ' ' _|' '|_ ' '   ')) >>> '2'"
"(self new ocrNumbers: #('    _  _ ' '  | _| _|' '  ||_  _|' '         ' '    _  _ ' '|_||_ |_ ' '  | _||_|' '         ' ' _  _  _ ' '  ||_||_|' '  ||_| _|' '         ')) >>> '123,456,789'"
"(self new ocrNumbers: #('    _  _     _  _  _  _  _  _ ' '  | _| _||_||_ |_   ||_||_|| |' '  ||_  _|  | _||_|  ||_| _||_|' '                              ')) >>> '1234567890'"
| w h lines rowBlocks refLines patterns result |
	w := 3.
	h := 4.
	lines := rows collect: [ :r | r asString ].
	lines size \\ h ~= 0 ifTrue: [
		self error: 'Number of lines must be a multiple of 4' ].
	(lines allSatisfy: [ :r | r size \\ w == 0 ]) ifFalse: [
		self error: 'Line width must be a multiple of 3' ].

	rowBlocks := lines size // h.

	refLines := { ' _     _  _     _  _  _  _  _ '.
	              '| |  | _| _||_||_ |_   ||_||_|'.
	              '|_|  ||_  _|  | _||_|  ||_| _|'.
	              '                              ' }.

	patterns := Dictionary new.
	0 to: 9 do: [ :i |
			| key |
			key := String streamContents: [ :s |
					       1 to: h do: [ :r |
						       s nextPutAll:
							       ((refLines at: r) copyFrom: i * w + 1 to: i * w + w) ] ].
			patterns at: key put: ('0123456789' at: i + 1) ].

	result := String streamContents: [ :out |
			          1 to: rowBlocks do: [ :rb |
					          rb > 1 ifTrue: [ out nextPut: $, ].
					          1 to: (lines at: rb) size // w do: [ :cb |
							          | blockKey |
							          blockKey := String streamContents: [ :s |
									                      1 to: h do: [ :r |
											                      s nextPutAll:
												                      ((lines at: rb - 1 * h + r)
													                       copyFrom: cb - 1 * w + 1
													                       to: cb - 1 * w + w) ] ].
							          out nextPut: (patterns at: blockKey ifAbsent: [ $? ]) ] ] ].

	^ result


"Tests"
testOcrNumbers
| result |

  result := (task ocrNumbers: #(' _ ' '| |' '|_|' '   ')).
  self assert: result equals: '0'.

  result := (task ocrNumbers: #('   ' '  |' '  |' '   ')).
  self assert: result equals: '1'.

  result := (task ocrNumbers: #('   ' '  _' '  |' '   ')).
  self assert: result equals: '?'.

  self
    should: [ task ocrNumbers: #(' _ ' '| |' '   ') ]
    raise: Error.

  self
    should: [ task ocrNumbers: #('    ' '   |' '   |' '    ') ]
    raise: Error.

  result := (task ocrNumbers: #('       _     _        _  _ ' '  |  || |  || |  |  || || |' '  |  ||_|  ||_|  |  ||_||_|' '                           ')).
  self assert: result equals: '110101100'.

  result := (task ocrNumbers: #('       _     _           _ ' '  |  || |  || |     || || |' '  |  | _|  ||_|  |  ||_||_|' '                           ')).
  self assert: result equals: '11?10?1?0'.

  result := (task ocrNumbers: #(' _ ' ' _|' '|_ ' '   ')).
  self assert: result equals: '2'.

  result := (task ocrNumbers: #(' _ ' ' _|' ' _|' '   ')).
  self assert: result equals: '3'.

  result := (task ocrNumbers: #('   ' '|_|' '  |' '   ')).
  self assert: result equals: '4'.

  result := (task ocrNumbers: #(' _ ' '|_ ' ' _|' '   ')).
  self assert: result equals: '5'.

  result := (task ocrNumbers: #(' _ ' '|_ ' '|_|' '   ')).
  self assert: result equals: '6'.

  result := (task ocrNumbers: #(' _ ' '  |' '  |' '   ')).
  self assert: result equals: '7'.

  result := (task ocrNumbers: #(' _ ' '|_|' '|_|' '   ')).
  self assert: result equals: '8'.

  result := (task ocrNumbers: #(' _ ' '|_|' ' _|' '   ')).
  self assert: result equals: '9'.

  result := (task ocrNumbers: #('    _  _     _  _  _  _  _  _ ' '  | _| _||_||_ |_   ||_||_|| |' '  ||_  _|  | _||_|  ||_| _||_|' '                              ')).
  self assert: result equals: '1234567890'.

  result := (task ocrNumbers: #('    _  _ ' '  | _| _|' '  ||_  _|' '         ' '    _  _ ' '|_||_ |_ ' '  | _||_|' '         ' ' _  _  _ ' '  ||_||_|' '  ||_| _|' '         ')).
  self assert: result equals: '123,456,789'