Skip to content

Commit

Permalink
Add counterclockwise rotation
Browse files Browse the repository at this point in the history
The default rotation is now clockwise¹. In the help widget, it now says: RotateR with k/↑, RotateL with g.

1 - both tetris.com and [tetris.fandom.com/wiki/Tetris_Guideline](https://tetris.fandom.com/wiki/Tetris_Guideline) use cw rotation for up
  • Loading branch information
siers committed Sep 16, 2023
1 parent 25560e5 commit 7361289
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 17 deletions.
32 changes: 18 additions & 14 deletions src/Tetris.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module Tetris
, Block(..)
, Coord
, Direction(..)
, RotationalDirection(..)
, Game(..)
, Tetrimino(..)
, Tetris
Expand Down Expand Up @@ -65,6 +66,9 @@ makeLenses ''Block
data Direction = Left | Right | Down
deriving (Eq, Show)

data RotationalDirection = Clockwise | Counterclockwise
deriving (Eq, Show)

-- | Board
--
-- If coordinate not present in map, yet in bounds, then it is empty,
Expand Down Expand Up @@ -132,19 +136,20 @@ boardHeight = 20
startOrigin :: Coord
startOrigin = V2 6 22

-- | Rotate block counter clockwise about origin
-- | Rotate the block clockwise (or counter-) around origin
-- *Note*: Strict unsafe rotation not respecting boundaries
-- Safety can only be assured within Game context
rotateRaw :: Block -> Block
rotateRaw b@(Block s o@(V2 xo yo) cs)
rotateRaw :: RotationalDirection -> Block -> Block
rotateRaw rd b@(Block s o@(V2 xo yo) cs)
| -- O doesn't need rotation
s == O = b
| -- I only has two orientations
s == I && V2 xo (yo + 1) `elem` cs = rotateWith clockwise
| otherwise = rotateWith counterclockwise
s == I && V2 xo (yo + 1) `elem` cs = rotateWith (direction Counterclockwise)
| s == I = rotateWith (direction Clockwise)
| otherwise = rotateWith (direction rd)
where
clockwise = (+ o) . cwperp . subtract o
counterclockwise = (+ o) . LV.perp . subtract o
direction Clockwise = (+ o) . cwperp . subtract o
direction Counterclockwise = (+ o) . LV.perp . subtract o
rotateWith dir = b & extra %~ fmap dir
cwperp (V2 x y) = V2 y (-x)

Expand Down Expand Up @@ -238,19 +243,18 @@ updateScore = do
latestOrZero Empty = 0
latestOrZero (_ :|> n) = n

-- | Handle counterclockwise block rotation (if possible)
-- Allows wallkicks: http://tetris.wikia.com/wiki/TGM_rotation
rotate :: Tetris ()
rotate = do
-- | Allows wallkicks: http://tetris.wikia.com/wiki/TGM_rotation
rotate :: RotationalDirection -> Tetris ()
rotate rd = do
blk <- use block
brd <- use board
let mblk = foldr (<|>) Nothing
$ mfilter (isValidBlockPosition brd)
. pure
. ($ blk)
<$> [ rotateRaw
, rotateRaw . translate Left
, rotateRaw . translate Right
<$> [ rotateRaw rd
, rotateRaw rd . translate Left
, rotateRaw rd . translate Right
]
forM_ mblk $ assign block

Expand Down
8 changes: 5 additions & 3 deletions src/UI/Game.hs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,9 @@ handleEvent (VtyEvent (V.EvKey V.KDown [])) = exec (shift Down)
handleEvent (VtyEvent (V.EvKey (V.KChar 'l') [])) = exec (shift Right)
handleEvent (VtyEvent (V.EvKey (V.KChar 'h') [])) = exec (shift Left)
handleEvent (VtyEvent (V.EvKey (V.KChar 'j') [])) = exec (shift Down)
handleEvent (VtyEvent (V.EvKey V.KUp [])) = exec rotate
handleEvent (VtyEvent (V.EvKey (V.KChar 'k') [])) = exec rotate
handleEvent (VtyEvent (V.EvKey V.KUp [])) = exec (rotate Clockwise)
handleEvent (VtyEvent (V.EvKey (V.KChar 'k') [])) = exec (rotate Clockwise)
handleEvent (VtyEvent (V.EvKey (V.KChar 'g') [])) = exec (rotate Counterclockwise)
handleEvent (VtyEvent (V.EvKey (V.KChar ' ') [])) =
guarded
(not . view paused)
Expand Down Expand Up @@ -262,7 +263,8 @@ drawHelp =
[ ("Left" , "h, ←")
, ("Right" , "l, →")
, ("Down" , "j, ↓")
, ("Rotate" , "k, ↑")
, ("RotateR", "k, ↑")
, ("RotateL", "g")
, ("Drop" , "space")
, ("Restart", "r")
, ("Pause" , "p")
Expand Down

0 comments on commit 7361289

Please sign in to comment.