On Fri, 7 Jan 2000 18:02:54 -0800 Dan Ingalls Dan.Ingalls@disney.com wrote:
The challenge is this: using Morphic and Squeak to best advantage, and leaving the appearance and feature set of Tetris essentially unchanged (and not just cramming everything into long lines with short variable names ;-), rewrite Tetris with a significantly lower number of linesOfCode.
Ok, here's mine (as a changeset to the 2.7 tetris). It
reduces #linesOfCode from 524 to 291 reduces #bytesOfCode from 4071 to 2399 "method for this metric is included"
Beyond this, I fear that readability will suffer.
Cheers, Bob
======== 'From Squeak2.7 of 5 January 2000 [latest update: #1761] on 8 January 2000 at 4:02:22 pm'! "Change Set: tetris2 Date: 8 January 2000 Author: Bob Arning
my entry in the TerseMan challenge"!
AlignmentMorph subclass: #Tetris instanceVariableNames: 'board scoreDisplay ' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Games'! Morph subclass: #TetrisBlock instanceVariableNames: 'angle shapeInfo board baseCellNumber ' classVariableNames: 'ShapeChoices ' poolDictionaries: '' category: 'Morphic-Games'! PasteUpMorph subclass: #TetrisBoard instanceVariableNames: 'paused gameOver delay score currentBlock game ' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Games'!
!ClassDescription methodsFor: 'private' stamp: 'RAA 1/8/2000 14:28'! bytesOfCode "InterpreterSimulator bytesOfCode 7476" "An approximate measure of lines of code. Includes comments, but excludes blank lines."
| space | space _ 0. self selectorsDo: [:sel | space _ space + (self compiledMethodAt: sel) size. ]. self isMeta ifTrue: [^ space] ifFalse: [^ space + self class bytesOfCode] " (SystemOrganization categories select: [:c | 'Fabrik*' match: c]) detectSum: [:c | (SystemOrganization superclassOrder: c) detectSum: [:cl | cl bytesOfCode]] "! !
!Tetris methodsFor: 'initialization' stamp: 'RAA 1/8/2000 14:38'! buildButtonTarget: aTarget label: aLabel selector: aSelector help: aString
^self rowForButtons addMorph: ( SimpleButtonMorph new target: aTarget; label: aLabel; actionSelector: aSelector; borderColor: #raised; borderWidth: 2; color: color )
! !
!Tetris methodsFor: 'initialization' stamp: 'RAA 1/7/2000 22:43'! initialize
super initialize. board _ TetrisBoard new game: self. color _ Color lightGray. orientation _ #vertical. centering _ #center. vResizing _ #shrinkWrap. hResizing _ #spaceFill. inset _ 3. self addMorphBack: self makeGameControls; addMorphBack: self makeMovementControls; addMorphBack: self showScoreDisplay; addMorphBack: board. board newGame.
! !
!Tetris methodsFor: 'initialization' stamp: 'RAA 1/7/2000 22:51'! makeGameControls
^self rowForButtons addMorph: (self buildButtonTarget: self label: 'Quit' selector: #delete help: 'quit'); addMorph: (self buildButtonTarget: board label: 'Pause' selector: #pause help: 'pause'); addMorph: (self buildButtonTarget: board label: 'New game' selector: #newGame help: 'new game')! !
!Tetris methodsFor: 'initialization' stamp: 'RAA 1/8/2000 14:03'! makeMovementControls
^self rowForButtons addMorph: (self buildButtonTarget: board label: '->' selector: #moveRight help: 'move to the right'); addMorph: (self buildButtonTarget: board label: ' ) ' selector: #rotateClockWise help: 'rotate clockwise'); addMorph: (self buildButtonTarget: board label: ' | ' selector: #dropAllTheWay help: 'drop'); addMorph: (self buildButtonTarget: board label: ' ( ' selector: #rotateAntiClockWise help: 'rotate anticlockwise'); addMorph: (self buildButtonTarget: board label: '<-' selector: #moveLeft help: 'move to the left')! !
!Tetris methodsFor: 'initialization' stamp: 'RAA 1/7/2000 21:48'! rowForButtons
^AlignmentMorph newRow color: color; borderWidth: 0; inset: 3; vResizing: #shrinkWrap; centering: #center ! !
!Tetris methodsFor: 'initialization' stamp: 'RAA 1/7/2000 21:50'! showScoreDisplay
^self rowForButtons hResizing: #rigid; addMorph: ( self wrapPanel: ( (scoreDisplay _ LedMorph new) digits: 4; extent: (4*10@15) ) label: 'Score:' ) ! !
!Tetris methodsFor: 'initialization' stamp: 'RAA 1/8/2000 14:38'! wrapPanel: anLedPanel label: aLabel "wrap an LED panel in an alignmentMorph with a label to its left"
^self rowForButtons color: color lighter; addMorph: anLedPanel; addMorph: (StringMorph contents: aLabel) ! !
!Tetris methodsFor: 'events' stamp: 'am 8/28/1999 14:22'! handlesMouseOver: evt ^ true ! !
!Tetris methodsFor: 'events' stamp: 'RAA 1/8/2000 15:42'! keyStroke: evt
| charValue | charValue _ evt keyCharacter asciiValue. charValue = 28 ifTrue: [board moveLeft]. charValue = 29 ifTrue: [board moveRight]. charValue = 30 ifTrue: [board rotateClockWise]. charValue = 31 ifTrue: [board rotateAntiClockWise]. charValue = 32 ifTrue: [board dropAllTheWay]. ! !
!Tetris methodsFor: 'events' stamp: 'am 8/28/1999 14:22'! mouseEnter: evt evt hand newKeyboardFocus: self ! !
!Tetris methodsFor: 'events' stamp: 'RAA 1/7/2000 22:37'! score: anInteger
scoreDisplay value: anInteger! !
!Tetris class methodsFor: 'as yet unclassified' stamp: 'RAA 1/7/2000 23:19'! colors
^{ Color r: 0.5 g: 0 b: 0. Color r: 0 g: 0.5 b: 0. Color r: 0 g: 0 b: 0.5. Color r: 0.5 g: 0.5 b: 0. Color r: 0.5 g: 0 b: 0.5. Color r: 0 g: 0.5 b: 0.5 } ! !
!TetrisBlock methodsFor: 'as yet unclassified' stamp: 'RAA 1/8/2000 15:58'! board: theBoard
board _ theBoard. 4 timesRepeat: [ self addMorph: ( RectangleMorph new color: color; extent: board cellSize; borderRaised ) ]. self positionCellMorphs.! !
!TetrisBlock methodsFor: 'as yet unclassified' stamp: 'RAA 1/8/2000 12:37'! dropByOne
^self moveDeltaX: 0 deltaY: 1 deltaAngle: 0! !
!TetrisBlock methodsFor: 'as yet unclassified' stamp: 'RAA 1/8/2000 15:33'! initialize
super initialize. bounds _ (2@2) negated extent: 1@1. "keep this puppy out of sight" shapeInfo _ self class shapeChoices atRandom. baseCellNumber _ (4 atRandom + 2) @ 1. angle _ 4 atRandom. color _ Tetris colors atRandom. ! !
!TetrisBlock methodsFor: 'as yet unclassified' stamp: 'RAA 1/8/2000 13:56'! moveDeltaX: deltaX deltaY: deltaY deltaAngle: deltaAngle
| delta |
delta _ deltaX @ deltaY. (shapeInfo atWrap: angle + deltaAngle) do: [ :offsetThisCell | (board emptyAt: baseCellNumber + offsetThisCell + delta) ifFalse: [^ false] ]. baseCellNumber _ baseCellNumber + delta. angle _ angle + deltaAngle - 1 \ 4 + 1. self positionCellMorphs. ^ true ! !
!TetrisBlock methodsFor: 'as yet unclassified' stamp: 'RAA 1/8/2000 13:41'! positionCellMorphs
(shapeInfo atWrap: angle) withIndexDo: [ :each :index | (submorphs at: index) position: (board originForCell: baseCellNumber + each) ]. fullBounds _ nil. self changed. ! !
!TetrisBlock class methodsFor: 'as yet unclassified' stamp: 'RAA 1/8/2000 15:29'! flipShapes: anArray
^OrderedCollection new add: anArray; add: (anArray collect: [ :each | each y negated @ each x]); add: (anArray collect: [ :each | each x negated @ each y negated]); add: (anArray collect: [ :each | each y @ each x negated]); yourself ! !
!TetrisBlock class methodsFor: 'as yet unclassified' stamp: 'RAA 1/8/2000 11:55'! includeInNewMorphMenu
^false! !
!TetrisBlock class methodsFor: 'as yet unclassified' stamp: 'RAA 1/8/2000 15:32'! shapeChoices
^ ShapeChoices ifNil: [ ShapeChoices _ { { { 0 @ 0 . 1 @ 0 . 0 @ 1 . 1 @ 1 } }. "square - one is sufficient here" self flipShapes: { 0 @ 0 . -1 @ 0 . 1 @ 0 . 0 @ -1 }. "T" { { 0 @ 0 . -1 @ 0 . 1 @ 0 . 2 @ 0 }. { 0 @ 0 . 0 @-1 . 0 @ 1 . 0 @ 2 } "long - two are sufficient here" }. self flipShapes: { 0 @ 0 . 0 @ -1 . 0 @ 1 . 1 @ 1 }. "L" self flipShapes: { 0 @ 0 . 0 @ -1 . 0 @ 1 . -1 @ 1 }. "inverted L" self flipShapes: { 0 @ 0 . -1 @ 0 . 0 @ -1 . 1 @ -1 }. "S" self flipShapes: { 0 @ 0 . 1 @ 0 . 0 @ -1 . -1 @ -1 } "Z" }. ] ! !
!TetrisBoard methodsFor: 'initialization' stamp: 'RAA 1/8/2000 13:26'! initialize
super initialize. resizeToFit _ false. bounds _ 0@0 extent: (self numColumns @ self numRows) * self cellSize + (1@1). color _ Color r: 0.8 g: 1.0 b: 1.0. ! !
!TetrisBoard methodsFor: 'stepping' stamp: 'RAA 1/8/2000 15:59'! step
(self ownerThatIsA: HandMorph) ifNotNil: [^self]. paused ifTrue: [^ self]. currentBlock ifNil: [ currentBlock _ TetrisBlock new. self addMorphFront: currentBlock. currentBlock board: self. ] ifNotNil: [ currentBlock dropByOne ifFalse: [self storePieceOnBoard] ]. ! !
!TetrisBoard methodsFor: 'stepping' stamp: 'AM 7/26/1999 16:07'! stepTime
^ delay! !
!TetrisBoard methodsFor: 'button actions' stamp: 'RAA 1/8/2000 14:03'! dropAllTheWay
self running ifFalse: [^ self]. [currentBlock dropByOne] whileTrue: [ self score: score + 1 ]. ! !
!TetrisBoard methodsFor: 'button actions' stamp: 'RAA 1/8/2000 11:17'! moveLeft
self running ifFalse: [^ self]. currentBlock moveDeltaX: -1 deltaY: 0 deltaAngle: 0. ! !
!TetrisBoard methodsFor: 'button actions' stamp: 'RAA 1/8/2000 11:17'! moveRight
self running ifFalse: [^ self]. currentBlock moveDeltaX: 1 deltaY: 0 deltaAngle: 0. ! !
!TetrisBoard methodsFor: 'button actions' stamp: 'RAA 1/8/2000 13:20'! newGame
self removeAllMorphs. gameOver _ paused _ false. delay _ 500. currentBlock _ nil. self score: 0. ! !
!TetrisBoard methodsFor: 'button actions' stamp: 'RAA 1/8/2000 11:16'! pause
gameOver ifTrue: [^ self]. paused _ paused not. ! !
!TetrisBoard methodsFor: 'button actions' stamp: 'RAA 1/8/2000 11:17'! rotateAntiClockWise
self running ifFalse: [^ self]. currentBlock moveDeltaX: 0 deltaY: 0 deltaAngle: -1. ! !
!TetrisBoard methodsFor: 'button actions' stamp: 'RAA 1/8/2000 11:17'! rotateClockWise
self running ifFalse: [^ self]. currentBlock moveDeltaX: 0 deltaY: 0 deltaAngle: 1. ! !
!TetrisBoard methodsFor: 'button actions' stamp: 'RAA 8/28/1999 22:31'! running
^currentBlock notNil and: [paused not]! !
!TetrisBoard methodsFor: 'other' stamp: 'RAA 1/8/2000 13:20'! checkForFullRows
| targetY morphsInRow bonus | self numRows to: 2 by: -1 do: [ :row | targetY _ (self originForCell: 1@row) y. [ morphsInRow _ self submorphsSatisfying: [ :each | each top = targetY]. morphsInRow size = self numColumns ] whileTrue: [ bonus _ (morphsInRow collect: [:each | each color]) asSet size = 1 ifTrue: [1000] ifFalse: [100]. self score: score + bonus. submorphs copy do: [ :each | each top = targetY ifTrue: [ each delete ]. each top < targetY ifTrue: [ each position: each position + (0@self cellSize y) ]. ]. ]. ].
! !
!TetrisBoard methodsFor: 'other' stamp: 'RAA 1/8/2000 13:59'! storePieceOnBoard
currentBlock submorphs do: [ :each | self addMorph: each. ((each top - self top) // self cellSize y) < 3 ifTrue: [ paused _ gameOver _ true. ]. ]. currentBlock delete. currentBlock _ nil. self checkForFullRows. self score: score + 10. delay _ delay - 2 max: 80.
! !
!TetrisBoard methodsFor: 'data' stamp: 'RAA 1/8/2000 13:16'! emptyAt: aPoint
| cellOrigin | (aPoint x between: 1 and: self numColumns) ifFalse: [^ false]. (aPoint y < 1) ifTrue: [^ true]. "handle early phases" (aPoint y <= self numRows) ifFalse: [^ false]. cellOrigin _ self originForCell: aPoint. ^(self submorphsSatisfying: [ :each | each topLeft = cellOrigin]) isEmpty
! !
!TetrisBoard methodsFor: 'data' stamp: 'RAA 8/28/1999 23:29'! numColumns
^10 ! !
!TetrisBoard methodsFor: 'data' stamp: 'RAA 8/28/1999 23:30'! numRows
^27 ! !
!TetrisBoard methodsFor: 'accessing' stamp: 'RAA 1/7/2000 22:34'! game: aTetris
game _ aTetris! !
!TetrisBoard methodsFor: 'accessing' stamp: 'RAA 1/7/2000 22:38'! score: aNumber
score _ aNumber. game score: score.! !
!TetrisBoard methodsFor: 'as yet unclassified' stamp: 'RAA 1/7/2000 23:12'! cellSize
^12@12! !
!TetrisBoard methodsFor: 'as yet unclassified' stamp: 'RAA 1/8/2000 13:11'! originForCell: aPoint
^aPoint - (1@1) * self cellSize + self position
! !
!TetrisBoard class methodsFor: 'as yet unclassified' stamp: 'RAA 1/7/2000 22:56'! includeInNewMorphMenu
^false! !
Tetris removeSelector: #mouseLeave:! Tetris removeSelector: #buildButton:target:label:selector:! Tetris removeSelector: #scoreDisplay! Tetris removeSelector: #board! TetrisBlock removeSelector: #tetris:! TetrisBlock class removeSelector: #squareShape! TetrisBlock class removeSelector: #teeShape! TetrisBlock class removeSelector: #invertedEllShape! TetrisBlock class removeSelector: #ellShape! TetrisBlock class removeSelector: #longShape! TetrisBlock class removeSelector: #zeeShape! TetrisBlock class removeSelector: #test:! TetrisBlock class removeSelector: #essShape! TetrisBoard removeSelector: #colors! TetrisBoard removeSelector: #checkRow! TetrisBoard removeSelector: #blockInfo! TetrisBoard removeSelector: #makeNewPiece! TetrisBoard removeSelector: #boardArray! TetrisBoard removeSelector: #dropByOne! TetrisBoard removeSelector: #paint! TetrisBoard removeSelector: #drop! TetrisBoard removeSelector: #withScoreDisplay:! TetrisBoard class removeSelector: #withScoreDisplay:! "Postscript: Leave the line above, and replace the rest of this comment by a useful one. Executable statements should follow this comment, and should be separated by periods, with no exclamation points (!!). Be sure to put any further comments in double-quotes, like this one."
StringHolder new contents: 'evaluate:
Tetris linesOfCode + TetrisBlock linesOfCode + TetrisBoard linesOfCode
Tetris bytesOfCode + TetrisBlock bytesOfCode + TetrisBoard bytesOfCode
while the original produced:
Tetris linesOfCode + TetrisBoard linesOfCode 524 Tetris bytesOfCode + TetrisBoard bytesOfCode 4071 '; openLabel: 'TerseMan Tetris challenge'!