I’m making an attempt to construct a tree implementation in Swift to characterize a chess sport.
A sport consists of a sequence of strikes however various strikes for a given board place are legitimate. I need to traverse the tree in my GUI which is why I added strategies to go to a particular node within the tree.
Nevertheless I’m combating getting the reminiscence mannequin proper. For my activity I need to hold a reference to the subsequent node and its dad or mum node for a given node. In my understanding these must be weak with a purpose to not introduce retain cycles. Nevertheless in doing so my implementation falls aside (as a result of I assume i don’t maintain a reference to the given node?).
Can any individual enlighten me on find out how to change my current implementation to make this work accurately? Once I take away the weak key phrase my take a look at succeeds nonetheless I don’t suppose that is proper as a result of once more due to potential retain cycles.
I eliminated a number of the implementation to make this extra readable:
import Basis
/// GameNode represents a node of a chess sport tree
public remaining class GameNode {
// MARK: - Properties
/// The place of the node
public let place: Place
/// Uniquely identifies a node
public let nodeId: UUID
/// Is the node on the prime of the tree
public let isTopNode: Bool
/// The chess transfer that will get from the dad or mum node to this one
public let transfer: Transfer?
/// An elective transfer annotation like !!, !?, ??
public let annotation: String?
/// A remark for the transfer
public let remark: String?
/// The dad or mum node
public inside(set) weak var dad or mum: GameNode?
/// Pointer to the principle variation
public inside(set) weak var subsequent: GameNode?
/// Different potential variations from this node
public inside(set) var variations: [GameNode]
// MARK: - Init
/// Creates a root node
public init(place: Place = .preliminary, remark: String? = nil) {
self.place = place
self.nodeId = UUID()
self.isTopNode = true
self.transfer = nil
self.annotation = nil
self.dad or mum = nil
self.remark = remark
self.subsequent = nil
self.variations = []
}
/// Creates a node which is the results of making a transfer in one other node
public init(place: Place, transfer: Transfer, dad or mum: GameNode, annotation: String? = nil, remark: String? = nil) {
self.place = place
self.nodeId = UUID()
self.isTopNode = false
self.transfer = transfer
self.annotation = annotation
self.dad or mum = dad or mum
self.remark = remark
self.subsequent = nil
self.variations = []
}
/// Reconstructs the transfer sequence from the beginning of the sport so far
public func reconstructMovesFromBeginning() -> [Move] {
if dad or mum?.isTopNode == true {
return [move].compactMap({ $0 })
}
var strikes = dad or mum?.reconstructMovesFromBeginning() ?? []
if let transfer {
strikes.append(transfer)
}
return strikes
}
}
public remaining class Sport {
public non-public(set) var present: GameNode
public init(root: GameNode = GameNode()) {
self.present = root
}
var root: GameNode? {
var tmp: GameNode? = present
whereas let currentTmp = tmp, !currentTmp.isTopNode {
tmp = currentTmp.dad or mum
}
return tmp
}
public var isAtEnd: Bool {
present.subsequent == nil
}
public func goBackward() {
guard let dad or mum = present.dad or mum else { return }
self.present = dad or mum
}
public func go(to node: GameNode) {
self.present = node
}
public func play(transfer: Transfer, remark: String? = nil, annotation: String? = nil) throws {
let newPosition = strive present.place.play(transfer: transfer)
let newNode = GameNode(place: newPosition, transfer: transfer, dad or mum: present, annotation: annotation, remark: remark)
if !isAtEnd {
present.subsequent?.variations.append(newNode)
} else {
present.subsequent = newNode
}
go(to: newNode)
}
public var uciPath: [String] {
present.reconstructMovesFromBeginning().map(.uci)
}
}
And the take a look at:
func testGameToUCIPath() throws {
let sport = strive Sport(fen: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
strive sport.play(transfer: .init(from: Squares.e2, to: Squares.e4))
strive sport.play(transfer: .init(from: Squares.e7, to: Squares.e5))
strive sport.play(transfer: .init(from: Squares.g1, to: Squares.f3))
strive sport.play(transfer: .init(from: Squares.b8, to: Squares.c6))
strive sport.play(transfer: .init(from: Squares.f1, to: Squares.b5))
XCTAssertEqual(sport.uciPath, ["e2e4", "e7e5", "g1f3", "b8c6", "f1b5"])
sport.goBackward()
XCTAssertEqual(sport.uciPath, ["e2e4", "e7e5", "g1f3", "b8c6"])
strive sport.play(transfer: .init(from: Squares.f1, to: Squares.c4))
XCTAssertEqual(sport.uciPath, ["e2e4", "e7e5", "g1f3", "b8c6", "f1c4"])
}