mirror of
https://github.com/chesskit-app/chesskit-swift.git
synced 2026-05-16 11:50:35 +00:00
Implement basic command line interface
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
.swiftpm
|
||||
.build
|
||||
Packages/
|
||||
Package.resolved
|
||||
|
||||
# Xcode
|
||||
/*.xcodeproj
|
||||
|
||||
@@ -15,8 +15,18 @@ let package = Package(
|
||||
products: [
|
||||
.library(name: "ChessKit", targets: ["ChessKit"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "http://github.com/dduan/Dye", from: "0.0.1")
|
||||
],
|
||||
targets: [
|
||||
.target(name: "ChessKit"),
|
||||
.executableTarget(
|
||||
name: "chesskit-cli",
|
||||
dependencies: [
|
||||
"ChessKit",
|
||||
.product(name: "Dye", package: "dye")
|
||||
]
|
||||
),
|
||||
.testTarget(name: "ChessKitTests", dependencies: ["ChessKit"])
|
||||
]
|
||||
)
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// CLI.swift
|
||||
// ChessKit
|
||||
//
|
||||
|
||||
import ChessKit
|
||||
import Dye
|
||||
|
||||
class CLI {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
private let help =
|
||||
"""
|
||||
USAGE:
|
||||
b, board Print the current position as a visual chess board
|
||||
c, clear Clear the visible area of the console
|
||||
m, move <san>... Execute moves on the board using standard algebraic notation
|
||||
h, help Print this help message
|
||||
p, position Print the current position as FEN
|
||||
r, reset [fen] Resets the board, optionally to a custom FEN position (standard position is the default)
|
||||
q, quit Ends ChessKit session
|
||||
|
||||
"""
|
||||
|
||||
private var board: Board
|
||||
private var output: OutputStream
|
||||
|
||||
// MARK: Initializer
|
||||
|
||||
init(board: Board = Board()) {
|
||||
self.board = board
|
||||
output = .standardOutput()
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
func startUp() {
|
||||
write("Starting ChessKit ♞...", as: .success)
|
||||
|
||||
write("\n")
|
||||
write("\(board)")
|
||||
write("\n\n")
|
||||
write(help)
|
||||
write("\n")
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func process(input: String) -> Command? {
|
||||
output.clear()
|
||||
let command = Command.match(input)
|
||||
|
||||
switch command {
|
||||
case nil:
|
||||
write("Unrecognized command ", as: .error)
|
||||
write("\(input)\n")
|
||||
case .exit:
|
||||
write("Goodbye\n", as: .success)
|
||||
case .board:
|
||||
write("\(board)\n")
|
||||
case .clear:
|
||||
print("\u{001B}[2J\u{001B}[H", terminator: "")
|
||||
case .help:
|
||||
write(help)
|
||||
case .position:
|
||||
write("\(FENParser.convert(position: board.position))\n", as: .info)
|
||||
case let .reset(fen):
|
||||
if let fen {
|
||||
if let position = FENParser.parse(fen: fen) {
|
||||
board = .init(position: position)
|
||||
write("\(board)\n")
|
||||
} else {
|
||||
write("Invalid FEN\n", as: .error)
|
||||
}
|
||||
} else {
|
||||
board = .init()
|
||||
write("\(board)\n")
|
||||
}
|
||||
case let .move(sans):
|
||||
sans.forEach {
|
||||
if let move = SANParser.parse(move: $0, in: board.position) {
|
||||
board.move(pieceAt: move.start, to: move.end)
|
||||
write("Moved ", as: .success)
|
||||
write("from ")
|
||||
write("\(move.start) ", as: .info)
|
||||
write("to ")
|
||||
write("\(move.end)\n", as: .info)
|
||||
} else {
|
||||
write("Invalid move ", as: .error)
|
||||
write("\($0)\n", as: .info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private enum MessageType {
|
||||
case error
|
||||
case info
|
||||
case success
|
||||
|
||||
var style: OutputStream.Style {
|
||||
switch self {
|
||||
case .error, .success: .bold
|
||||
case .info: []
|
||||
}
|
||||
}
|
||||
|
||||
var foreground: OutputStream.Color {
|
||||
switch self {
|
||||
case .error: .intenseRed
|
||||
case .info: .intenseCyan
|
||||
case .success: .intenseGreen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func write(_ string: String, as type: MessageType? = nil) {
|
||||
output.color.foreground = type?.foreground
|
||||
output.style = type?.style ?? []
|
||||
output.write(string)
|
||||
output.clear()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Command.swift
|
||||
// ChessKit
|
||||
//
|
||||
|
||||
enum Command: Equatable {
|
||||
case exit
|
||||
case board
|
||||
case clear
|
||||
case help
|
||||
case position
|
||||
case reset(fen: String? = nil)
|
||||
case move(sans: [String])
|
||||
|
||||
static func match(_ input: String) -> Command? {
|
||||
let input = input.split(separator: " ").map(String.init)
|
||||
guard let command = input.first else { return nil }
|
||||
|
||||
let args = input.count > 1 ? Array(input.dropFirst()) : [String]()
|
||||
|
||||
switch command {
|
||||
case "exit", "quit", "q":
|
||||
return .exit
|
||||
case "board", "b":
|
||||
return .board
|
||||
case "clear", "c":
|
||||
return .clear
|
||||
case "help", "h":
|
||||
return .help
|
||||
case "position", "p":
|
||||
return .position
|
||||
case "reset", "r":
|
||||
let fen = args.joined(separator: " ")
|
||||
if fen.isEmpty {
|
||||
return .reset(fen: nil)
|
||||
} else {
|
||||
return .reset(fen: fen)
|
||||
}
|
||||
case "move", "m":
|
||||
return .move(sans: args)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// main.swift
|
||||
// ChessKit
|
||||
//
|
||||
|
||||
var cli = CLI()
|
||||
cli.startUp()
|
||||
|
||||
runLoop: while true {
|
||||
print(">> ", terminator: "")
|
||||
guard let input = readLine(), !input.isEmpty else { continue }
|
||||
|
||||
if cli.process(input: input) == .exit {
|
||||
break runLoop
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user