{"id":109,"date":"2015-02-03T21:54:14","date_gmt":"2015-02-03T21:54:14","guid":{"rendered":"http:\/\/cpscotti.com\/blog\/?p=109"},"modified":"2015-02-03T21:59:23","modified_gmt":"2015-02-03T21:59:23","slug":"swiftris-the-last-minute-move","status":"publish","type":"post","link":"http:\/\/cpscotti.com\/blog\/?p=109","title":{"rendered":"Swiftris &#8211; The last minute move"},"content":{"rendered":"<p>This post is of the Swiftris series so if you have no idea what I&#8217;m talking about, read <a title=\"this other post\" href=\"http:\/\/cpscotti.com\/blog\/?p=104\">this<\/a>.<\/p>\n<p>If you&#8217;ve played a lot of tetris in your life you understand that not all is over when one &#8216;tetrino&#8217; touches the settled rubble at the bottom of the pit &#8211; when that happens you still have 1 &#8216;tick&#8217; to move it sideways or even spin the piece if it fits! Let&#8217;s call that the &#8216;last move rule&#8217;. Swiftris ADDS code to prevent you from doing that &#8211; it prematurely settles the tetrino.<\/p>\n<p>Somewhere in your Swiftris.swift file you&#8217;ll have something like:<\/p>\n<p>[sourcecode language=&#8221;javascript&#8221; title=&#8221;Swiftris.swift excerpt&#8221;]<br \/>\n    func letShapeFall() {<br \/>\n        if let shape = fallingShape {<br \/>\n            shape.lowerShapeByOneRow()<br \/>\n            if detectIllegalPlacement() {<br \/>\n                shape.raiseShapeByOneRow()<br \/>\n                if detectIllegalPlacement() {<br \/>\n                    endGame()<br \/>\n                } else {<br \/>\n                    settleShape()<br \/>\n                }<br \/>\n            } else {<br \/>\n                delegate?.gameShapeDidMove(self)<br \/>\n                if detectTouch() {<br \/>\n                    settleShape()<br \/>\n                }<br \/>\n            }<br \/>\n        }<br \/>\n    }<br \/>\n[\/sourcecode]<\/p>\n<p>&#8216;letShapeFall()&#8217; is the one method executed over and over when a shape is falling and &#8216;detectIllegalPlacement()&#8217; is the method that verifies either the board contains an illegal placement.<br \/>\nAdditionally, it introduces the &#8216;detectTouch()&#8217; which simply duplicates the functionality of &#8216;detectIllegalPlacement()&#8217; but implemented separately.. for no apparent reason.<\/p>\n<p>&#8216;detectTouch()&#8217; is only used to &#8216;settle&#8217; a tetrino when it touches the rubble at the bottom of the pit. Basically, only used to &#8216;ruin&#8217; the &#8216;last move&#8217; rule. Suppose we force &#8216;detectTouch()&#8217; to always return false, that way, the &#8216;life&#8217; of that tetrino will be extended by one &#8216;tick&#8217; cycle only because as soon as &#8216;letShapeFall()&#8217; is called again at the next cycle &#8216;detectIllegalPlacement()&#8217; will return true and effectively &#8216;settleShape()&#8217;. You see, the current implementation offers two different paths for the &#8216;settleShape()&#8217; calls where logically they should happen under the same circumstances. By any standards that stinks a bit. So, let&#8217;s just get rid of it. Try changing your code to this:<\/p>\n<p>[sourcecode language=&#8221;javascript&#8221; title=&#8221;Swiftris.swift excerpt&#8221;]<br \/>\n    func letShapeFall() {<br \/>\n        if let shape = fallingShape {<br \/>\n            shape.lowerShapeByOneRow()<br \/>\n            if detectIllegalPlacement() {<br \/>\n                shape.raiseShapeByOneRow()<br \/>\n                if detectIllegalPlacement() {<br \/>\n                    endGame()<br \/>\n                } else {<br \/>\n                    settleShape()<br \/>\n                }<br \/>\n            } else {<br \/>\n                delegate?.gameShapeDidMove(self)<\/p>\n<p>                \/\/ Comment out this.<br \/>\n                \/\/ if detectTouch() {<br \/>\n                \/\/     settleShape()<br \/>\n                \/\/ }<br \/>\n            }<br \/>\n        }<br \/>\n    }<br \/>\n[\/sourcecode]<\/p>\n<p>Try it out.. everything still works fine and now the life of the Tetrinos at their last hopeless moment of fall is a tiny bit extended! Yay!<\/p>\n<p>Now, if you look around you&#8217;ll see that there&#8217;s a LOT of code that is there for nothing.. detectTouch() is never used, and so isn&#8217;t &#8216;bottomBlocksForOrientations&#8217; or &#8216;bottomBlocks&#8217; in Shape.swift and all it&#8217;s child (JShape, LineShape, LShape, SquareShape, SShape, TShape, ZShape). You can safely remove all that and it&#8217;s about 80 lines.. that serve no purpose but be buggy and confuse ppl \ud83d\ude42<\/p>\n<p>For example, some shape implementations become really simple and short. TShape for example:<\/p>\n<p>[sourcecode language=&#8221;javascript&#8221; title=&#8221;TShape.swift&#8221;]<br \/>\nclass TShape:Shape {<br \/>\n    \/* &#8230; *\/<\/p>\n<p>    override var blockRowColumnPositions: [Orientation: Array&lt;(cDiff: Int, rDiff: Int)&gt;] {<br \/>\n        return [<br \/>\n            Orientation.Zero:       [(1, 0), (0, 1), (1, 1), (2, 1)],<br \/>\n            Orientation.Ninety:     [(2, 1), (1, 0), (1, 1), (1, 2)],<br \/>\n            Orientation.OneEighty:  [(1, 2), (0, 1), (1, 1), (2, 1)],<br \/>\n            Orientation.TwoSeventy: [(0, 1), (1, 0), (1, 1), (1, 2)]<br \/>\n        ]<br \/>\n    }<br \/>\n}<br \/>\n[\/sourcecode]<\/p>\n<p>Try out the game after the changes.. it all works fine.. I hope!<\/p>\n<p>Another problem with Swiftris is that whenever you complete a line it treats all blocks above that line as independently free falling &#8211; that&#8217;s not how original Tetris works and it makes the game a heck easier than the original. In the original, the fallen blocks all fall as a single connected mass leaving whatever small holes you had below it untouched. Plus, the logic for the fallen blocks, something intrinsic to the game is written inside Swiftris.swift but it&#8217;s called from the GameViewController.swift.. a pretty awkward implementation of any Model-View-Controller or Model-View-ViewModel pattern they got in there &#8211; it works and all but.. My next post will get a bit into that.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is of the Swiftris series so if you have no idea what I&#8217;m talking about, read this. If you&#8217;ve played a lot of tetris in your life you understand that not all is over when one &#8216;tetrino&#8217; touches the settled rubble at the bottom of the pit &#8211; when that happens you still [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[23,22,24],"tags":[26,27,28,25],"class_list":["post-109","post","type-post","status-publish","format-standard","hentry","category-ios","category-swift","category-swiftris","tag-bloc","tag-ios","tag-swift","tag-swiftris"],"_links":{"self":[{"href":"http:\/\/cpscotti.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/109","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/cpscotti.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/cpscotti.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/cpscotti.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/cpscotti.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=109"}],"version-history":[{"count":4,"href":"http:\/\/cpscotti.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/109\/revisions"}],"predecessor-version":[{"id":113,"href":"http:\/\/cpscotti.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/109\/revisions\/113"}],"wp:attachment":[{"href":"http:\/\/cpscotti.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=109"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/cpscotti.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=109"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/cpscotti.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=109"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}