Sage is a cross-platform chess library for Swift.

Related tags

Games Sage
Overview

Deprecated

This project is no longer in development. I am currently developing a chess engine, Hexe. It is written in Rust, which is very similar to Swift in many ways. There also exists Hexe.swift, a Swift wrapper for Hexe.

Sage is not a chess engine; it's a move generator. Hexe, on the other hand, is able to both generate moves and evaluate them.


Sage

Swift 2.2 | 3.0 Platforms Gitter Apache 2.0 License

CocoaPods Carthage Swift Package Manager Documented

Sage is a cross-platform chess library for Swift.

Development happens in the develop branch.

Build Status

Branch Status
master Build Status
develop Build Status

Features

  • Chess game management
  • Chess board structuring
  • Move generation / validation
  • En passant and castling
  • Pawn promotions
  • FEN for games and boards
  • PGN parsing and exporting
  • Documentation

Installation

Compatibility

  • Platforms:
    • macOS 10.9+
    • iOS 8.0+
    • watchOS 2.0+
    • tvOS 9.0+
    • Linux
  • Xcode 7.3 and 8.0
  • Swift 2.2 and 3.0

Install Using Swift Package Manager

The Swift Package Manager is a decentralized dependency manager for Swift.

  1. Add the project to your Package.swift.

    import PackageDescription
    
    let package = Package(
        name: "MyAwesomeProject",
        dependencies: [
            .Package(url: "https://github.com/nvzqz/Sage.git",
                     majorVersion: 2)
        ]
    )
  2. Import the Sage module.

    import Sage

Install Using CocoaPods

CocoaPods is a centralized dependency manager for Objective-C and Swift. Go here to learn more.

  1. Add the project to your Podfile.

    use_frameworks!
    
    pod 'Sage', '~> 2.0.0'

    If you want to be on the bleeding edge, replace the last line with:

    pod 'Sage', :git => 'https://github.com/nvzqz/Sage.git'
  2. Run pod install and open the .xcworkspace file to launch Xcode.

  3. Import the Sage framework.

    import Sage

Install Using Carthage

Carthage is a decentralized dependency manager for Objective-C and Swift.

  1. Add the project to your Cartfile.

    github "nvzqz/Sage"
    
  2. Run carthage update and follow the additional steps in order to add Sage to your project.

  3. Import the Sage framework.

    import Sage

Install Manually

  1. Download and drop the /Sources folder into your project.

  2. Congratulations!

Usage

Game Management

Running a chess game can be as simple as setting up a loop.

import Sage

let game = Game()

while !game.isFinished {
    let move = ...
    try game.execute(move: move)
}

Move Execution

Moves for a Game instance can be executed with execute(move:) and its unsafe (yet faster) sibling, execute(uncheckedMove:).

The execute(uncheckedMove:) method assumes that the passed move is legal. It should only be called if you absolutely know this is true. Such a case is when using a move returned by availableMoves(). Otherwise use execute(move:), which checks the legality of the passed move.

Move Generation

Sage is capable of generating legal moves for the current player with full support for special moves such as en passant and castling.

  • availableMoves() will return all moves currently available.

  • movesForPiece(at:) will return all moves for a piece at a square.

  • movesBitboardForPiece(at:) will return a Bitboard containing all of the squares a piece at a square can move to.

Move Validation

Sage can also validate whether a move is legal with the isLegal(move:) method for a Game state.

The execute(move:) family of methods calls this method, so it would be faster to execute the move directly and catch any error from an illegal move.

Undo and Redo Moves

Move undo and redo operations are done with the undoMove() and redoMove() methods. The undone or redone move is returned.

To just check what moves are to be undone or redone, the moveToUndo() and moveToRedo() methods are available.

Promotion Handling

The execute(move:promotion:) method takes a closure that returns a promotion piece kind. This allows for the app to prompt the user for a promotion piece or perform any other operations before choosing a promotion piece kind.

try game.execute(move: move) {
    ...
    return .queen
}

The closure is only executed if the move is a pawn promotion. An error is thrown if the promotion piece kind cannot promote a pawn, such as with a king or pawn.

A piece kind can also be given without a closure. The default is a queen.

try game.execute(move: move, promotion: .queen)

Pretty Printing

The Board and Bitboard types both have an ascii property that can be used to print a visual board.

let board = Board()

board.ascii
//   +-----------------+
// 8 | r n b q k b n r |
// 7 | p p p p p p p p |
// 6 | . . . . . . . . |
// 5 | . . . . . . . . |
// 4 | . . . . . . . . |
// 3 | . . . . . . . . |
// 2 | P P P P P P P P |
// 1 | R N B Q K B N R |
//   +-----------------+
//     a b c d e f g h

board.occupiedSpaces.ascii
//   +-----------------+
// 8 | 1 1 1 1 1 1 1 1 |
// 7 | 1 1 1 1 1 1 1 1 |
// 6 | . . . . . . . . |
// 5 | . . . . . . . . |
// 4 | . . . . . . . . |
// 3 | . . . . . . . . |
// 2 | 1 1 1 1 1 1 1 1 |
// 1 | 1 1 1 1 1 1 1 1 |
//   +-----------------+
//     a b c d e f g h

Forsyth–Edwards Notation

The Game.Position and Board types can both generate a FEN string.

let game = Game()

game.position.fen()
// rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1

game.board.fen()
// rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR

They can also be initialized from a FEN string.

assert(Board(fen: game.board.fen()) == game.board)

assert(Game.Position(fen: game.position.fen()) == game.position)

Iterating Through a Board

The Board type conforms to Sequence, making iterating through its spaces seamless.

for space in Board() {
    if let piece = space.piece {
        print("\(piece) at \(space.square)")
    }
}

Squares to Moves

Sequence and Square have two methods that return an array of moves that go from/to self to/from the parameter.

[.a1, .h3, .b5].moves(from: .b4)
// [b4 >>> a1, b4 >>> h3, b4 >>> b5]

[.c3, .d2, .f1].moves(to: .a6)
// [c3 >>> a6, d2 >>> a6, f1 >>> a6]

Square.d4.moves(from: [.c2, .f8, .h2])
// [c2 >>> d4, f8 >>> d4, h2 >>> d4]

Square.a4.moves(to: [.c3, .d4, .f6])
// [a4 >>> c3, a4 >>> d4, a4 >>> f6]

Playground Usage

To use Sage.playground, first open Sage.xcodeproj and build the OS X target. You can then use the playground from within the project.

Board Quick Look

Board conforms to the CustomPlaygroundQuickLookable protocol.

Playground quick look

Donation

I work on this in my free time and do my best to make it as great as it can be. If you want to help me keep pushing out awesome libraries like this, a donation would be greatly appreciated. 😄

paypal

License

Sage is published under version 2.0 of the Apache License.

Comments
  • No such module, when importing Sage in playground

    No such module, when importing Sage in playground

    Hi, I am very excited about this project! 👍

    I git clone master branch and opened the Playground but I get a compile error on the

    import Sage
    

    Line, with the error "No such module 'Sage'".

    Any ideas?

    opened by Sajjon 4
  • How to get captured piece after a move?

    How to get captured piece after a move?

    First of all, awesome work you have here!

    Now, how do I get the captured piece after a move?

    I've been looking at the move history, but I wonder if you though about an easier way.

    enhancement 
    opened by cesarvarela 3
  • Fixed issue in `Game` copying

    Fixed issue in `Game` copying

    I don't know whether it is deliberate for not adding the code self.enPassantTarget = game.enPassantTarget. But not adding this line will cause the following issue:

    Let's suppose a game A is in an "En Passant" state, that is, the next player who moves the piece can make an en passant move. Apparently, calling myGame.isLegal(move: myEnPassantMove) will return true. But if we call myGame.copy().isLegal(move: myEnPassantMove), it will return false which is not act as "expected".

    opened by Hengyu 1
  • I want to build Computer opponent

    I want to build Computer opponent

    i found that Minimax with alpha beta pruning is best way to implement chess AI, but i just wonder if i could using some other open source that could do this ?

    opened by hunght 1
  • Castling rights not updated after rook capture

    Castling rights not updated after rook capture

    The castling rights don't seem to be updated when a rook is captured. Here's a quick test I put together:

    func testCastlingRightsAfterRookCapture() {
        let startFen = "rnbqkbnr/pppppp1p/8/8/1P4p1/8/PBPPPPPP/RN1QKBNR w KQkq - 0 1"
        let startPosition = Game.Position(fen: startFen)!
        let game = try! Game(position: startPosition)
        let move = Move(start: .b2, end: .h8)
        try! game.execute(move: move)
        XCTAssertFalse(game.castlingRights.contains(.blackKingside))
    }
    

    It looks like _execute(uncheckedMove:promotion:) is only updating castling rights when moving rooks or kings right now.

    Interestingly, I was able to generate a fen that included invalid castling rights but I couldn't initialize a Position using that same fen (a PositionError.missingRook was thrown). It might be good to include position validation during fen generation too.

    Thanks for the fantastic library. Very impressive work!

    opened by robmaceachern 1
  • Manual Installation

    Manual Installation

    CocoaPods and Carthage are awesome tools and make our life really easier, but there are some devs who still don't know how to use them.

    It would be cool to add the Manual installation guide in your README.md. You can take a look at my iOS Readme Template to see how you can do it.

    opened by lfarah 1
  • Add a Gitter chat badge to README.md

    Add a Gitter chat badge to README.md

    nvzqz/Sage now has a Chat Room on Gitter

    @nvzqz has just created a chat room. You can visit it here: https://gitter.im/nvzqz/Sage.

    This pull-request adds this badge to your README.md:

    Gitter

    If my aim is a little off, please let me know.

    Happy chatting.

    PS: Click here if you would prefer not to receive automatic pull-requests from Gitter in future.

    opened by gitter-badger 0
  • Board initializer use of piece.hashValue crashes under Swift 4.2

    Board initializer use of piece.hashValue crashes under Swift 4.2

    Swift 4.2 introduced random seeding for hashing so that hash values are really different each time the program runs. The board initializer function in line 360 of Board.swift uses hash values as identifier for an array with 12 elements which under Swift 4.2/XCode 10 causes an index out of range error:

    /// Create a chess board.
    ///
    /// - parameter variant: The variant to populate the board for. Won't populate if `nil`. Default is `Standard`.
    public init(variant: Variant? = ._standard) {
        #if swift(>=3)
            _bitboards = Array(repeating: 0, count: 12)
        #else
            _bitboards = Array(count: 12, repeatedValue: 0)
        #endif
        if let variant = variant {
            for piece in Piece.all {
                _bitboards[piece.hashValue] = Bitboard(startFor: piece)
            }
            if variant.isUpsideDown {
                for index in _bitboards.indices {
                    _bitboards[index].flipVertically()
                }
            }
        }
    }
    

    The critical code is in the for loop: _bitboards[piece.hashValue] = Bitboard(startFor: piece) now crashes the program. I understand why the error is there but don't know why this program ran without error under Swift 4.1 because the hash value should (also under Swift 4.1) be a kind of random Int which may be bigger than the maximum index number 11 of the _bitboards array. @nvzqz seemed to have used the piece.hashValue rather like an identifier or rawValue.

    Any help in explaining why the program didn't crash under Swift 4.1 and in rectifying the error would be appreciated!

    opened by SuperGeroy 4
  • game.kingIsChecked wrong after own king was in check before delivering check

    game.kingIsChecked wrong after own king was in check before delivering check

    When your own king is in check and your move removes the check and at the same time delivers check to the opponent king then the game.kingIsChecked variable is wrong, it is false but should be true.

    I stumbled upon the error with the following position FEN: 2K1r3/3P1k2/8/8/8/8/8/2R5 w - - 0 1 where White is in check already. After the promotion move dxe8Q+ the black king is in check but kingIsChecked is false. After the promotion move d8N+ the black king is also in check but kingIsChecked is false.

    The error has nothing to do with the promotion, I tried it with different positions and the common condition for the error is that the own king is in check directly before you put the enemy king in check.

    opened by SuperGeroy 1
  • Fixed bugs in `_redoMove()` and `_undoMove()`

    Fixed bugs in `_redoMove()` and `_undoMove()`

    En Passant move

    An inconsistency may be created by performing undoMove() and redoMove() several times.

    For example: Let a game starts with the following moves: e2->e4, f7->f5, e4->f5. Then, let's perform undoMove and redoMove. Then check the available moves for black pawn in g7. An inconsistency may be expected.

    Reason: In private function _redoMove(), we calls _execute(uncheckedMove: promotion:) and passes the move that will be redone. But the property enPassantMove has not been updated in _redoMove. So there will be an inconsistency in the future calls performed on the game.

    Solution: Updates the enPassantMove in _redoMove().

    King is checked

    kingIsChecked may returns the wrong value.

    Reason: This is caused by copying a wrong value to _undoHistory in the function _undoMove(). Solution: Pass the correct value to _undoHistory.append(:).

    opened by Hengyu 1
Releases(v2.5.2)
  • v2.5.2(Sep 22, 2016)

    Release Notes

    Fixes

    • Fixes issue regarding legal move generation for king in check due to error in moves available for the attacking piece
    Source code(tar.gz)
    Source code(zip)
  • v2.5.1(Aug 18, 2016)

  • v2.5.0(Aug 17, 2016)

    Release Notes

    New Features

    • Added pinned(for:) method to Board
    • Added between(_:), isBetween(start:end:) and line(with:) methods to Square
    • Added hasMoreThanOne to Bitboard

    Enhancements

    • Made legal move generation/checking faster for king pieces

    Fixes

    • Fixed conditions for castling so that a king cannot castle in check and it can't castle through squares that are being attacked
    Source code(tar.gz)
    Source code(zip)
  • v2.4.1(Aug 8, 2016)

  • v2.4.0(Aug 6, 2016)

    Release Notes

    New Features

    • Added init(position:whitePlayer:blackPlayer:variant:) to Game #7

    Enhancements

    • Greatly improved performance of attackers(to:color:) method for Board
    • Improved performance for pieceCount(for:) for Board
    • Improved performance for contains(_:) for Bitboard
    Source code(tar.gz)
    Source code(zip)
  • v2.3.0(Aug 1, 2016)

  • v2.2.0(Jul 30, 2016)

    Release Notes

    New Features

    • Added execute(uncheckedMove:) family of methods to Game
    • Added initializer with moves array to Game

    Enhancements

    • Improved performance for bitboard(for color: Color) method for Board
    Source code(tar.gz)
    Source code(zip)
  • v2.1.0(Jul 24, 2016)

    Release Notes

    New Features

    • Added static white, black, kingside, and queenside constants to CastlingRights and CastlingRights.Right
    • Added canCastle(for:) methods to CastlingRights that take a Color or Board.Side
    • Added init(color:) and init(side:) to CastlingRights
    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(Jul 21, 2016)

  • v2.0.0(Jul 17, 2016)

    Release Notes

    Enhancements

    • Performance greatly improved when performing operations with Board

    New Features

    • Most significant bit operations to Bitboard:

      • Properties: msb, msbIndex, msbSquare
      • Methods: popMSB(), popMSBIndex(), popMSBSquare()
    • Board initializer from arrays of piece characters

      Board(pieces: [["r", "n", "b", "q", "k", "b", "n", "r"],
                     ["p", "p", "p", "p", "p", "p", "p", "p"],
                     [" ", " ", " ", " ", " ", " ", " ", " "],
                     [" ", " ", " ", " ", " ", " ", " ", " "],
                     [" ", " ", " ", " ", " ", " ", " ", " "],
                     [" ", " ", " ", " ", " ", " ", " ", " "],
                     ["P", "P", "P", "P", "P", "P", "P", "P"],
                     ["R", "N", "B", "Q", "K", "B", "N", "R"]])
      
    • Parsing PGN string data with PGN(parse:)

    • Exporting PGN string data with exported()

    • New Player struct

    Breaking Changes

    • Piece has been changed to a struct type with nested a Kind type
      • Values such as isKing and relativeValue now belong to Kind
    • The argument-less bitboard() method for Board has been changed to occupiedSpaces
    • Replaced Game.Mode with two Player instances for a game

    Fixes

    • Calling redoMove() would sometimes cause a crash if the Game instance had no available moves (e.g. was over).
    • The Board playground view for iOS and tvOS was flipped vertically
    • canPromote(_:) for Piece didn't take king into account
    • Castling rights weren't restored in undoMove()
    • execute(move:) didn't check the promotion piece's kind
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Jul 3, 2016)

Owner
Nikolai Vazquez
conducts curses, breaks things, and uses the oxford comma
Nikolai Vazquez
A simple Chess game for iOS, written in Swift

Swift Chess This is a simple chess game for iPhone and iPad, designed for novice players. It features a very simple AI that plays much like a beginner

Nick Lockwood 135 Jan 6, 2023
ShogibanKit is a framework (not yet) for implementing complex Japanese Chess (Shogii) in Swift. No UI, nor AI.

ShogibanKit Framework Shogi, or Japanese Chess, is based on very complex rules, and it is hard to implement all basic rules. This ShogibanKit aims to

Kaz Yoshikawa 62 Jul 16, 2022
An iOS / Mac view controller for chess boards

FDChessboardView Features High resolution graphics Customizable themes and game graphics Supports all single board chess variants: suicide, losers, at

William Entriken 75 Jul 16, 2022
Open Source release of the code for the iOS retro 80s platform game Loot Raider

Loot Raider iOS v1.7 (c) 2018 Infusions of Grandeur - Written By: Eric Crichlow Background Loot Raider is the "spiritual successor" to a game named "G

Eric Crichlow 9 Feb 9, 2022
iOS drop-in library presenting a 2048-style game

iOS-2048 iOS drop-in library presenting a clean-room Objective-C/Cocoa implementation of the game 2048. Screenshot Instructions The included sample ap

Austin Zheng 321 Dec 22, 2022
App created in UIKit in order to control users' games library

App created in UIKit in order to control users' games library. It's developed in Swift 5 with Storyboard, and relies in a ASP.Net API backend.

Alliston 2 Mar 15, 2022
Swift-WordleSolver - Solve and analyze Wordle games. Command-line tool written in Swift

Swift-WordleSolver - Solve and analyze Wordle games. Command-line tool written in Swift

Tobi Schweiger 0 Jan 26, 2022
Imagine Engine - a fast, high performance Swift 2D game engine for Apple's platforms

Welcome to Imagine Engine, an ongoing project that aims to create a fast, high performance Swift 2D game engine for Apple's platforms that is also a j

John Sundell 1.8k Jan 3, 2023
🐦 Flappy Bird reincarnation [Swift 5.3, GameplayKit, SpriteKit, iOS 12].

?? Flappy Bird reincarnation [Swift 5.3, GameplayKit, SpriteKit, iOS 12].

Astemir Eleev 290 Dec 27, 2022
A game engine built with SDL and Swift.

Lark A game engine made with Swift and SDL. This is a pre-alpha work-in-progress. Don't try to use this unless you really know what you're doing. I ba

June Bash 41 Mar 11, 2022
A Swift package for Raylib. Builds Raylib from source so no need to fiddle with libraries.

A Swift package for Raylib. Builds Raylib from source so no need to fiddle with libraries. Just add as a dependency in you game package and go!

Strega's Gate 62 Dec 29, 2022
2048 for Swift

swift-2048 A working port of iOS-2048 to Apple's new Swift language. Like the original Objective-C version, swift-2048 does not rely upon SpriteKit. S

Austin Zheng 3.1k Dec 27, 2022
swift implementation of flappy bird. More at fullstackedu.com

FlappySwift An implementation of Flappy Bird in Swift for iOS 8. Notes We're launching a course Game Programming with Swift If you are interested in e

newline (formerly Fullstack.io) 9.5k Dec 29, 2022
iOS Swift Game - Push SpriteKit to the limit

iOS Swift Project - Legend Wings - EverWing's Mini Clone EverWing is a popular action game. Survive as much you can, earn gold, and upgrade/purchase n

Wong Guan 512 Dec 20, 2022
Simple memory game written in Swift 4 using VIPER Architecture.

Viper Memory Game Simple memory game written in Swift 4.2 using VIPER Architecture. The Memory Game is a deck of cards where the user needs to find ma

Mati 23 Jun 6, 2022
A project introducing you to Swift

?? My first Memory ?? ?? An introduction to iOS development with Swift. A memory game implementation fetching images from Instagram. This project aims

Alexander Cyon 72 Sep 5, 2022
2D ECS game engine in 100% Swift + SwiftUI for iOS, macOS, tvOS

OctopusKit A 2D game engine based on ECS and written in 100% Swift for iOS, macOS and tvOS. If you've tried making a game in Swift while sticking to t

null 335 Dec 12, 2022
Letter Quiz game with swift for ios

LETTERQUIZ Title: LetterQuiz Category: Game Genre: Trivia, Puzzle, Quiz Platform: IOS Language: Swift 4.0 Available at: Developer: Martin Bruland Crea

Martin Bruland 1 Nov 25, 2022
Swift framework for working with Tiled assets in SpriteKit

SKTiled is a framework for integrating Tiled assets with Apple's SpriteKit, built from the ground up with Swift. This project began life as an exercis

Michael Fessenden 235 Dec 20, 2022