diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Releases
 
+## chessIO 0.6.1.1
+
+- Fix [half-move counter not being reset on pawn push or capture](https://github.com/mlang/chessIO/issues/2) (Thanks to Oleg Grenrus)
+- Fix [toFEN not emitting a dash when only king moved](https://github.com/mlang/chessIO/issues/3) (Thanks to Oleg Grenrus)
+
 ## chessIO 0.6.1.0
 
 - Add enPassantSquare (thanks Tochi Obudulu).
diff --git a/app/cboard.hs b/app/cboard.hs
--- a/app/cboard.hs
+++ b/app/cboard.hs
@@ -142,12 +142,7 @@
           Nothing -> outputStrLn "Sorry, no hint available"
         loop
       | "pass" == input -> do
-        unlessM (searching e) $ do
-          (bmc, _) <- search e [movetime (sec 2)]
-          hr <- lift $ gets hintRef
-          externalPrint <- getExternalPrint
-          tid <- liftIO . forkIO $ doBestMove externalPrint hr bmc e
-          lift $ modify' $ \s -> s { mover = Just tid }
+        unlessM (searching e) $ searchBestMove
         loop
       | input `elem` ["analyze", "analyse"] -> do
         unlessM (searching e) $ do
diff --git a/app/cbookview.hs b/app/cbookview.hs
--- a/app/cbookview.hs
+++ b/app/cbookview.hs
@@ -57,7 +57,7 @@
 previousPosition st = foldl' doPly (st^.initialPosition) (st^.treePos & label & NonEmpty.init)
 
 targetSquare :: St -> Int
-targetSquare = plyTarget . NonEmpty.last . label . _treePos
+targetSquare = plyTarget . NonEmpty.last . label . (^. treePos)
 
 elemList :: Eq a => n -> a -> [a] -> L.List n a
 elemList n x xs = L.list n (Vec.fromList xs) 1 & L.listSelectedL .~ i where
@@ -179,7 +179,7 @@
       <=> hBox [str "Board style (+/- to change): ", style]
       <=> hBox [str "Up/Down (kj) = change ply, Left/Right (hl) = back/forward"
                , hCenter $ str " "
-               , str "ESC = Quit"
+               , str "ESC (q) = Quit"
                ]
     style = vLimit 1 $ L.renderList drawStyle True (st^.boardStyle)
     drawStyle foc (n, _) = putCursorIf foc BoardStyle (0,0) $ str n
@@ -197,7 +197,7 @@
   appAttrMap = const $ attrMap V.defAttr
              [(selectedAttr, V.white `on` V.green)
              ]
-  appChooseCursor = F.focusRingCursor _focusRing
+  appChooseCursor = F.focusRingCursor (^. focusRing)
 
 loadForest :: (Position -> Forest Ply) -> Position -> St -> Maybe St
 loadForest f p st = case f p of
diff --git a/app/polyplay.hs b/app/polyplay.hs
--- a/app/polyplay.hs
+++ b/app/polyplay.hs
@@ -113,7 +113,7 @@
 polyplay rt = do
   (h, o) <- play rt
   let wname = maybe "Unknown" decodeUtf8 $
-              case (active rt) of Player e _ -> name e
+              case active rt of Player e _ -> name e
   let bname = wname
   let g = gameFromForest [ ("White", wname)
                          , ("Black", bname)
diff --git a/book/twic-9g.bin b/book/twic-9g.bin
# file too large to diff: book/twic-9g.bin
diff --git a/chessIO.cabal b/chessIO.cabal
--- a/chessIO.cabal
+++ b/chessIO.cabal
@@ -1,13 +1,13 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.33.0.
+-- This file has been generated from package.yaml by hpack version 0.34.4.
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: de80a5b1f8b0f93212816ed6945a0c402a0bf5ecdc280d53f32d0c195ce4a588
+-- hash: 3fcf399df92e8a53a4f22341832c3dc5a45222618873d4edd74b61fca3a35598
 
 name:           chessIO
-version:        0.6.1.0
+version:        0.6.1.1
 synopsis:       Basic chess library
 description:    A simple and fast library for generating legal chess moves. Also includes a module for communication with external processes that speak the UCI (Universal Chess Interface) protocol, a PGN parser/pretty printer, and Polyglot opening book support. On top of that, provides a console frontend program (cboard) that can be used to interactively play against UCI engines, and a terminal program (cbookview) to explore commonly played chess openings.
 category:       Game
@@ -44,7 +44,26 @@
       Paths_chessIO
   hs-source-dirs:
       src
-  default-extensions: BangPatterns BinaryLiterals BlockArguments DeriveGeneric FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NumericUnderscores RecordWildCards OverloadedStrings PatternSynonyms TupleSections TypeApplications TypeFamilies ViewPatterns
+  default-extensions:
+      BangPatterns
+      BinaryLiterals
+      BlockArguments
+      DeriveGeneric
+      FlexibleContexts
+      FlexibleInstances
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NumericUnderscores
+      RecordWildCards
+      OverloadedStrings
+      PatternSynonyms
+      TupleSections
+      TypeApplications
+      TypeFamilies
+      ViewPatterns
   ghc-options: -O2
   build-depends:
       MonadRandom
@@ -72,7 +91,26 @@
       Paths_chessIO
   hs-source-dirs:
       app
-  default-extensions: BangPatterns BinaryLiterals BlockArguments DeriveGeneric FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NumericUnderscores RecordWildCards OverloadedStrings PatternSynonyms TupleSections TypeApplications TypeFamilies ViewPatterns
+  default-extensions:
+      BangPatterns
+      BinaryLiterals
+      BlockArguments
+      DeriveGeneric
+      FlexibleContexts
+      FlexibleInstances
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NumericUnderscores
+      RecordWildCards
+      OverloadedStrings
+      PatternSynonyms
+      TupleSections
+      TypeApplications
+      TypeFamilies
+      ViewPatterns
   ghc-options: -O2 -threaded
   build-depends:
       MonadRandom
@@ -103,7 +141,26 @@
       Paths_chessIO
   hs-source-dirs:
       app
-  default-extensions: BangPatterns BinaryLiterals BlockArguments DeriveGeneric FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NumericUnderscores RecordWildCards OverloadedStrings PatternSynonyms TupleSections TypeApplications TypeFamilies ViewPatterns
+  default-extensions:
+      BangPatterns
+      BinaryLiterals
+      BlockArguments
+      DeriveGeneric
+      FlexibleContexts
+      FlexibleInstances
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NumericUnderscores
+      RecordWildCards
+      OverloadedStrings
+      PatternSynonyms
+      TupleSections
+      TypeApplications
+      TypeFamilies
+      ViewPatterns
   ghc-options: -O2 -threaded
   build-depends:
       MonadRandom
@@ -138,7 +195,26 @@
       Paths_chessIO
   hs-source-dirs:
       app
-  default-extensions: BangPatterns BinaryLiterals BlockArguments DeriveGeneric FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NumericUnderscores RecordWildCards OverloadedStrings PatternSynonyms TupleSections TypeApplications TypeFamilies ViewPatterns
+  default-extensions:
+      BangPatterns
+      BinaryLiterals
+      BlockArguments
+      DeriveGeneric
+      FlexibleContexts
+      FlexibleInstances
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NumericUnderscores
+      RecordWildCards
+      OverloadedStrings
+      PatternSynonyms
+      TupleSections
+      TypeApplications
+      TypeFamilies
+      ViewPatterns
   ghc-options: -O2
   build-depends:
       MonadRandom
@@ -170,7 +246,26 @@
       Paths_chessIO
   hs-source-dirs:
       test
-  default-extensions: BangPatterns BinaryLiterals BlockArguments DeriveGeneric FlexibleContexts FlexibleInstances GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses MultiWayIf NamedFieldPuns NumericUnderscores RecordWildCards OverloadedStrings PatternSynonyms TupleSections TypeApplications TypeFamilies ViewPatterns
+  default-extensions:
+      BangPatterns
+      BinaryLiterals
+      BlockArguments
+      DeriveGeneric
+      FlexibleContexts
+      FlexibleInstances
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NumericUnderscores
+      RecordWildCards
+      OverloadedStrings
+      PatternSynonyms
+      TupleSections
+      TypeApplications
+      TypeFamilies
+      ViewPatterns
   ghc-options: -O2 -threaded -rtsopts "-with-rtsopts=-N -s"
   build-depends:
       MonadRandom
diff --git a/src/Game/Chess/Internal.hs b/src/Game/Chess/Internal.hs
--- a/src/Game/Chess/Internal.hs
+++ b/src/Game/Chess/Internal.hs
@@ -35,7 +35,7 @@
 import Text.Read (readMaybe)
 
 capturing :: Position -> Ply -> Maybe PieceType
-capturing pos@Position{flags} (unpack -> (_, to, _))
+capturing pos@Position{flags} (plyTarget -> to)
   | (flags .&. epMask) `testBit` to = Just Pawn
   | otherwise = snd <$> pieceAt pos to
 
@@ -148,8 +148,11 @@
  where
   showColor White = "w"
   showColor Black = "b"
-  showCst 0 = "-"
-  showCst x = snd . wks . wqs . bks . bqs $ (x, "") where
+  showCst x
+    | str == "" = "-"
+    | otherwise = str
+   where 
+    str = snd . wks . wqs . bks . bqs $ (x, "")
     wks (v, xs) | v `testMask` crwKs = (v, 'K':xs)
                 | otherwise          = (v, xs)
     wqs (v, xs) | v `testMask` crwQs = (v, 'Q':xs)
@@ -325,12 +328,22 @@
 
 -- | An unsafe version of 'doPly'.  Only use this if you are sure the given move
 -- can be applied to the position.  This is useful if the move has been generated
--- by the 'moves' function.
+-- by the 'legalPlies' function.
 unsafeDoPly :: Position -> Ply -> Position
-unsafeDoPly pos@Position{color = White, halfMoveClock} m =
-  (unsafeDoPly' pos m) { color = Black, halfMoveClock = succ halfMoveClock }
-unsafeDoPly pos@Position{color = Black, moveNumber, halfMoveClock} m =
-  (unsafeDoPly' pos m) { color = White, moveNumber = succ moveNumber, halfMoveClock = succ halfMoveClock }
+unsafeDoPly pos@Position{color, halfMoveClock, moveNumber} m =
+  pos' { color = opponent color
+       , halfMoveClock = if isCapture pos m || isPawnPush pos m
+                         then 0
+                         else succ halfMoveClock
+       , moveNumber = if color == Black
+                      then succ moveNumber
+                      else moveNumber
+       }
+ where
+  pos' = unsafeDoPly' pos m
+  isPawnPush p m = case pieceAt p (plySource m) of
+    Just (_, Pawn) -> True
+    _         -> False
 
 unsafeDoPly' :: Position -> Ply -> Position
 unsafeDoPly' pos@Position{qbb, flags} m@(unpack -> (from, to, promo))
diff --git a/src/Game/Chess/SAN.hs b/src/Game/Chess/SAN.hs
--- a/src/Game/Chess/SAN.hs
+++ b/src/Game/Chess/SAN.hs
@@ -39,7 +39,7 @@
 import Game.Chess.Internal ( Castle(Queenside, Kingside),
                              Ply, Position(color, moveNumber), Color(Black, White),
                              PieceType(..), isCapture, pieceAt,
-                             promoteTo, unpack, doPly, unsafeDoPly, legalPlies,
+                             promoteTo, plySource, plyTarget, unpack, doPly, unsafeDoPly, legalPlies,
                              inCheck, canCastleKingside, canCastleQueenside,
                              wKscm, wQscm, bKscm, bQscm )
 import Game.Chess.Internal.Square (toIndex, toRF, toCoord)
@@ -153,9 +153,8 @@
     case filter (pieceFrom p) ms of
       [] -> fail $ show (color pos) <> " has no " <> show p <> " which could be moved"
       ms' -> target p ms'
-  pieceFrom p (moveFrom -> from) = p == snd (fromJust (pieceAt pos from))
-  moveFrom (unpack -> (from, _, _)) = from
-  target p ms = coords p ms >>= \m@(unpack -> (_, to, _)) -> case p of
+  pieceFrom p (plySource -> from) = p == snd (fromJust (pieceAt pos from))
+  target p ms = coords p ms >>= \m@(plyTarget -> to) -> case p of
     Pawn | lastRank to -> promoteTo m <$> promotion
     _ -> pure m
   coords p ms = choice $ fmap (uncurry (<$) . fmap chunk) $
@@ -253,9 +252,9 @@
     | capture = "x" <> toCoord to
     | otherwise = toCoord to
   ms = filter (isMoveTo to) lms
-  isMoveTo sq (unpack -> (_, to', _)) = sq == to'
-  fEq (unpack -> (from', _, _)) = from' `mod` 8 == fromFile
-  rEq (unpack -> (from', _, _)) = from' `div` 8 == fromRank
+  isMoveTo sq (plyTarget -> to') = sq == to'
+  fEq (plySource -> from') = from' `mod` 8 == fromFile
+  rEq (plySource -> from') = from' `div` 8 == fromRank
   (fromRank, fromFile) = toRF from
   fileChar i = chr $ (i `mod` 8) + ord 'a'
   rankChar i = chr $ (i `div` 8) + ord '1'
@@ -302,8 +301,8 @@
   ms = filter movesTo $ legalPlies pos
   movesTo (unpack -> (from', to', _)) =
     fmap snd (pieceAt pos from') == Just piece && to' == to
-  fEq (unpack -> (from', _, _)) = from' `mod` 8 == fromFile
-  rEq (unpack -> (from', _, _)) = from' `div` 8 == fromRank
+  fEq (plySource -> from') = from' `mod` 8 == fromFile
+  rEq (plySource -> from') = from' `div` 8 == fromRank
   (fromRank, fromFile) = toRF from
   fileChar i = chr $ (i `mod` 8) + ord 'a'
   rankChar i = chr $ (i `div` 8) + ord '1'
