Changed visibility from package to interal
This commit is contained in:
@@ -29,19 +29,19 @@ public class Board: CustomStringConvertible {
|
|||||||
public private(set) var fen: Fen
|
public private(set) var fen: Fen
|
||||||
|
|
||||||
public func setBoard() throws {
|
public func setBoard() throws {
|
||||||
var file: UInt8 = 1
|
var file: Int8 = 1
|
||||||
var rank: UInt8 = 8
|
var rank: Int8 = 8
|
||||||
var index: UInt8 = 0
|
var index: Int8 = 0
|
||||||
var b: [Square] = squares
|
var b: [Square] = squares
|
||||||
for c in fen.placement {
|
for c in fen.placement {
|
||||||
let r = (8 - Int(rank)) % 8
|
let r = 8 - Int(rank)
|
||||||
if c == "/" {
|
if c == "/" {
|
||||||
if file != 9 {
|
if file != 9 {
|
||||||
throw Fen.FenError.NotAppropriateLength(n: file, column: index)
|
throw Fen.FenError.NotAppropriateLength(n: file, column: index)
|
||||||
}
|
}
|
||||||
rank -= 1
|
rank -= 1
|
||||||
file = 0
|
file = 0
|
||||||
} else if c.isWholeNumber, let n = UInt8(String(c)) {
|
} else if c.isWholeNumber, let n = Int8(String(c)) {
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
throw Fen.FenError.NumberTooSmall(n: n, column: index)
|
throw Fen.FenError.NumberTooSmall(n: n, column: index)
|
||||||
} else if n > 8 {
|
} else if n > 8 {
|
||||||
@@ -82,20 +82,22 @@ public class Board: CustomStringConvertible {
|
|||||||
file += 1
|
file += 1
|
||||||
index += 1
|
index += 1
|
||||||
}
|
}
|
||||||
|
#if DEBUG
|
||||||
print(b)
|
print(b)
|
||||||
|
#endif
|
||||||
squares = b
|
squares = b
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init(fen: Fen =
|
public required init(fen: Fen =
|
||||||
.init(fen: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
|
.init(fen: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
|
||||||
) {
|
) {
|
||||||
var rank: UInt8 = 8
|
var rank: Int8 = 8
|
||||||
self.fen = fen
|
self.fen = fen
|
||||||
for i in 0...63 {
|
for i in 0...63 {
|
||||||
let index = UInt8(i)
|
let index = Int8(i)
|
||||||
let square = Square(
|
let square = Square(
|
||||||
position: .init(file: (index % 8) + 1, rank: rank),
|
position: .init(file: (index % 8) + 1, rank: rank),
|
||||||
index: index,
|
index: i,
|
||||||
color: index % 2 != rank % 2 ? .Black : .White)
|
color: index % 2 != rank % 2 ? .Black : .White)
|
||||||
squares.append(square)
|
squares.append(square)
|
||||||
if (index + 1) % 8 == 0 {
|
if (index + 1) % 8 == 0 {
|
||||||
|
|||||||
@@ -64,10 +64,10 @@ public struct Fen: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum FenError: Error {
|
public enum FenError: Error {
|
||||||
case InvalidCharacter(c: String, column: UInt8)
|
case InvalidCharacter(c: String, column: Int8)
|
||||||
case NumberTooBig(n: UInt8, column: UInt8)
|
case NumberTooBig(n: Int8, column: Int8)
|
||||||
case NumberTooSmall(n: UInt8, column: UInt8)
|
case NumberTooSmall(n: Int8, column: Int8)
|
||||||
case NotAppropriateLength(n: UInt8, column: UInt8)
|
case NotAppropriateLength(n: Int8, column: Int8)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var description: String {
|
public var description: String {
|
||||||
|
|||||||
@@ -1,31 +1,40 @@
|
|||||||
package class Bishop: Piece {
|
internal final class Bishop: Piece, DiagonalMoves {
|
||||||
package weak var board: Board?
|
internal weak var board: Board?
|
||||||
package var kind: Kind = .Bishop
|
internal var kind: Kind = .Bishop
|
||||||
|
|
||||||
package var unicodeRepresentation: String {
|
internal var unicodeRepresentation: String {
|
||||||
return color == .Black ? "♝" : "♗"
|
return color == .Black ? "♝" : "♗"
|
||||||
}
|
}
|
||||||
package var color: Color
|
internal var color: Color
|
||||||
|
|
||||||
package var position: Square.Position
|
internal var position: Square.Position
|
||||||
|
|
||||||
package var pseudoLegalPositions: [Square.Position] {
|
internal var pseudoLegalPositions: [Square.Position] {
|
||||||
return []
|
return getDiagonalMoves(from: position)
|
||||||
}
|
}
|
||||||
|
|
||||||
package var legalPositions: [Square.Position] {
|
internal var legalPositions: [Square.Position] {
|
||||||
return pseudoLegalPositions.filter { isLegal(pos: $0) }
|
return pseudoLegalPositions.filter { isLegal(on: $0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
package func move(dst: Square.Position) -> Bool {
|
internal func move(to dst: Square.Position) -> Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
package func isLegal(pos: Square.Position) -> Bool {
|
internal func isLegal(on pos: Square.Position) -> Bool {
|
||||||
|
if let board = board, let s = board[pos] {
|
||||||
|
if let p = s.piece {
|
||||||
|
if p.color == color { return false }
|
||||||
|
if p.kind == .King {
|
||||||
|
// TODO: Notify board of check
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
package init(color: Color, on position: Square.Position) {
|
internal init(color: Color, on position: Square.Position) {
|
||||||
self.color = color
|
self.color = color
|
||||||
self.position = position
|
self.position = position
|
||||||
}
|
}
|
||||||
|
|||||||
19
Sources/Engine/Pieces/CommonMoves.swift
Normal file
19
Sources/Engine/Pieces/CommonMoves.swift
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
internal protocol DiagonalMoves {
|
||||||
|
func getDiagonalMoves(from pos: Square.Position) -> [Square.Position]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DiagonalMoves {
|
||||||
|
func getDiagonalMoves(from pos: Square.Position) -> [Square.Position] {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal protocol LinearMoves {
|
||||||
|
func getLinearMoves(from pos: Square.Position) -> [Square.Position]
|
||||||
|
}
|
||||||
|
|
||||||
|
extension LinearMoves {
|
||||||
|
func getLinearMoves(from pos: Square.Position) -> [Square.Position] {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,31 +1,41 @@
|
|||||||
package class King: Piece {
|
internal final class King: Piece {
|
||||||
package weak var board: Board?
|
internal weak var board: Board?
|
||||||
package var kind: Kind = .King
|
internal var kind: Kind = .King
|
||||||
|
|
||||||
package var unicodeRepresentation: String {
|
internal var unicodeRepresentation: String {
|
||||||
return color == .Black ? "♛" : "♕"
|
return color == .Black ? "♛" : "♕"
|
||||||
}
|
}
|
||||||
package var color: Color
|
internal var color: Color
|
||||||
|
|
||||||
package var position: Square.Position
|
internal var position: Square.Position
|
||||||
|
|
||||||
package var pseudoLegalPositions: [Square.Position] {
|
internal var pseudoLegalPositions: [Square.Position] {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
package var legalPositions: [Square.Position] {
|
internal var legalPositions: [Square.Position] {
|
||||||
return pseudoLegalPositions.filter { isLegal(pos: $0) }
|
return pseudoLegalPositions.filter { isLegal(on: $0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
package func move(dst: Square.Position) -> Bool {
|
internal func move(to dst: Square.Position) -> Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
package func isLegal(pos: Square.Position) -> Bool {
|
internal func isLegal(on pos: Square.Position) -> Bool {
|
||||||
|
if let board = board, let s = board[pos] {
|
||||||
|
if let p = s.piece {
|
||||||
|
if p.color == color { return false }
|
||||||
|
if p.kind == .King {
|
||||||
|
// TODO: Notify board of check
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
package init(color: Color, on position: Square.Position) {
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
internal init(color: Color, on position: Square.Position) {
|
||||||
self.color = color
|
self.color = color
|
||||||
self.position = position
|
self.position = position
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,54 @@
|
|||||||
package class Knight: Piece {
|
internal final class Knight: Piece {
|
||||||
package weak var board: Board?
|
internal weak var board: Board?
|
||||||
package var kind: Kind = .Knight
|
internal var kind: Kind = .Knight
|
||||||
|
|
||||||
package var unicodeRepresentation: String {
|
internal var unicodeRepresentation: String {
|
||||||
return color == .Black ? "♞" : "♘"
|
return color == .Black ? "♞" : "♘"
|
||||||
}
|
}
|
||||||
package var color: Color
|
internal var color: Color
|
||||||
|
|
||||||
package var position: Square.Position
|
internal var position: Square.Position
|
||||||
|
|
||||||
package var pseudoLegalPositions: [Square.Position] {
|
internal var pseudoLegalPositions: [Square.Position] {
|
||||||
return []
|
let directions: [Square.Position] = [
|
||||||
|
.init(file: position.file + 1, rank: position.rank + 2),
|
||||||
|
.init(file: position.file - 1, rank: position.rank + 2),
|
||||||
|
.init(file: position.file + 1, rank: position.rank - 2),
|
||||||
|
.init(file: position.file - 1, rank: position.rank - 2),
|
||||||
|
.init(file: position.file + 2, rank: position.rank + 1),
|
||||||
|
.init(file: position.file - 2, rank: position.rank + 1),
|
||||||
|
.init(file: position.file + 2, rank: position.rank - 1),
|
||||||
|
.init(file: position.file - 2, rank: position.rank - 1)
|
||||||
|
]
|
||||||
|
return directions.filter {
|
||||||
|
let index = $0.index
|
||||||
|
return index < 0 || index > 63
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
package var legalPositions: [Square.Position] {
|
internal var legalPositions: [Square.Position] {
|
||||||
return pseudoLegalPositions.filter { isLegal(pos: $0) }
|
return pseudoLegalPositions.filter { isLegal(on: $0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
package func move(dst: Square.Position) -> Bool {
|
internal func move(to dst: Square.Position) -> Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
package func isLegal(pos: Square.Position) -> Bool {
|
internal func isLegal(on pos: Square.Position) -> Bool {
|
||||||
|
if let board = board, let s = board[pos] {
|
||||||
|
if let p = s.piece {
|
||||||
|
if p.color == color { return false }
|
||||||
|
if p.kind == .King {
|
||||||
|
// TODO: Notify board of check
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
package init(color: Color, on position: Square.Position) {
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
internal init(color: Color, on position: Square.Position) {
|
||||||
self.color = color
|
self.color = color
|
||||||
self.position = position
|
self.position = position
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,32 @@
|
|||||||
package class Pawn: Piece {
|
internal final class Pawn: Piece {
|
||||||
package var kind: Kind = .Pawn
|
internal var kind: Kind = .Pawn
|
||||||
package weak var board: Board?
|
internal weak var board: Board?
|
||||||
package var color: Color
|
internal var color: Color
|
||||||
package var position: Square.Position
|
internal var position: Square.Position
|
||||||
package var pseudoLegalPositions: [Square.Position] {
|
internal var pseudoLegalPositions: [Square.Position] {
|
||||||
return []
|
let sign: Int8 = color == .Black ? -1 : 1
|
||||||
|
let rank = Int8(position.rank)
|
||||||
|
let directions: [Square.Position] = [
|
||||||
|
.init(file: position.file, rank: rank + 1 * sign),
|
||||||
|
.init(file: position.file, rank: rank + 2 * sign),
|
||||||
|
.init(file: position.file + 1, rank: rank + 1 * sign),
|
||||||
|
.init(file: position.file - 1, rank: rank + 1 * sign)
|
||||||
|
]
|
||||||
|
// TODO: Handle en passant
|
||||||
|
return directions.filter {
|
||||||
|
let index = $0.index
|
||||||
|
return index < 1 || index > 63
|
||||||
}
|
}
|
||||||
package var legalPositions: [Square.Position] {
|
}
|
||||||
return pseudoLegalPositions.filter { isLegal(pos: $0) }
|
internal var legalPositions: [Square.Position] {
|
||||||
|
pseudoLegalPositions.filter { isLegal(on: $0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
package var unicodeRepresentation: String {
|
internal var unicodeRepresentation: String {
|
||||||
return color == .Black ? "♟" : "♙"
|
color == .Black ? "♟" : "♙"
|
||||||
}
|
}
|
||||||
|
|
||||||
package func move(dst: Square.Position) -> Bool {
|
internal func move(to dst: Square.Position) -> Bool {
|
||||||
guard board != nil else {
|
guard board != nil else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -33,18 +45,22 @@ package class Pawn: Piece {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
package func isLegal(pos: Square.Position) -> Bool {
|
internal func isLegal(on pos: Square.Position) -> Bool {
|
||||||
// TODO: Handle "En-Passant"
|
// TODO: Handle "En-Passant"
|
||||||
if let board = board, let s = board[pos] {
|
if let board = board, let s = board[pos] {
|
||||||
if let p = s.piece {
|
if let p = s.piece {
|
||||||
if p.color == color { return false }
|
if p.color == color { return false }
|
||||||
|
if p.kind == .King {
|
||||||
|
// TODO: Notify board of check
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
package init(
|
internal init(
|
||||||
color: Color, on position: Square.Position
|
color: Color, on position: Square.Position
|
||||||
) {
|
) {
|
||||||
self.color = color
|
self.color = color
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
public enum Kind: String, CaseIterable {
|
internal enum Kind: String, CaseIterable {
|
||||||
case Pawn, Knight, Bishop, Rook, Queen, King
|
case Pawn, Knight, Bishop, Rook, Queen, King
|
||||||
|
|
||||||
public var value: Int8 {
|
internal var value: Int8 {
|
||||||
switch self {
|
switch self {
|
||||||
case .Pawn: 1
|
case .Pawn: 1
|
||||||
case .Bishop: 3
|
case .Bishop: 3
|
||||||
@@ -12,7 +12,7 @@ public enum Kind: String, CaseIterable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static subscript(_ c: Character) -> (Self, Color)? {
|
internal static subscript(_ c: Character) -> (Self, Color)? {
|
||||||
let v = c.uppercased()
|
let v = c.uppercased()
|
||||||
|
|
||||||
guard
|
guard
|
||||||
@@ -39,7 +39,7 @@ public enum Kind: String, CaseIterable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol Piece {
|
internal protocol Piece {
|
||||||
var board: Board? { get }
|
var board: Board? { get }
|
||||||
var color: Color { get }
|
var color: Color { get }
|
||||||
var unicodeRepresentation: String { get }
|
var unicodeRepresentation: String { get }
|
||||||
@@ -47,6 +47,6 @@ public protocol Piece {
|
|||||||
var position: Square.Position { get }
|
var position: Square.Position { get }
|
||||||
var pseudoLegalPositions: [Square.Position] { get }
|
var pseudoLegalPositions: [Square.Position] { get }
|
||||||
var legalPositions: [Square.Position] { get }
|
var legalPositions: [Square.Position] { get }
|
||||||
func move(dst: Square.Position) -> Bool
|
func move(to dst: Square.Position) -> Bool
|
||||||
func isLegal(pos: Square.Position) -> Bool
|
func isLegal(on pos: Square.Position) -> Bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,42 @@
|
|||||||
package class Queen: Piece {
|
internal final class Queen: Piece, LinearMoves, DiagonalMoves {
|
||||||
package weak var board: Board?
|
internal weak var board: Board?
|
||||||
package var kind: Kind = .Queen
|
internal var kind: Kind = .Queen
|
||||||
|
|
||||||
package var unicodeRepresentation: String {
|
internal var unicodeRepresentation: String {
|
||||||
return color == .Black ? "♛" : "♕"
|
return color == .Black ? "♛" : "♕"
|
||||||
}
|
}
|
||||||
package var color: Color
|
internal var color: Color
|
||||||
|
|
||||||
package var position: Square.Position
|
internal var position: Square.Position
|
||||||
|
|
||||||
package var pseudoLegalPositions: [Square.Position] {
|
internal var pseudoLegalPositions: [Square.Position] {
|
||||||
return []
|
return getDiagonalMoves(from: position) + getLinearMoves(from: position)
|
||||||
}
|
}
|
||||||
|
|
||||||
package var legalPositions: [Square.Position] {
|
internal var legalPositions: [Square.Position] {
|
||||||
return pseudoLegalPositions.filter { isLegal(pos: $0) }
|
return pseudoLegalPositions.filter { isLegal(on: $0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
package func move(dst: Square.Position) -> Bool {
|
internal func move(to dst: Square.Position) -> Bool {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
package func isLegal(pos: Square.Position) -> Bool {
|
internal func isLegal(on pos: Square.Position) -> Bool {
|
||||||
|
if let board = board, let s = board[pos] {
|
||||||
|
if let p = s.piece {
|
||||||
|
if p.color == color { return false }
|
||||||
|
if p.kind == .King {
|
||||||
|
// TODO: Notify board of check
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
package init(color: Color, on position: Square.Position) {
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
internal init(color: Color, on position: Square.Position) {
|
||||||
self.color = color
|
self.color = color
|
||||||
self.position = position
|
self.position = position
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,41 @@
|
|||||||
package class Rook: Piece {
|
internal final class Rook: Piece, LinearMoves {
|
||||||
package weak var board: Board?
|
internal weak var board: Board?
|
||||||
package var kind: Kind = .Rook
|
internal var kind: Kind = .Rook
|
||||||
|
|
||||||
package var unicodeRepresentation: String {
|
internal var unicodeRepresentation: String {
|
||||||
return color == .Black ? "♜" : "♖"
|
return color == .Black ? "♜" : "♖"
|
||||||
}
|
}
|
||||||
package var color: Color
|
internal var color: Color
|
||||||
|
|
||||||
package var position: Square.Position
|
internal var position: Square.Position
|
||||||
|
|
||||||
package var pseudoLegalPositions: [Square.Position] {
|
internal var pseudoLegalPositions: [Square.Position] {
|
||||||
return []
|
return getLinearMoves(from: position)
|
||||||
}
|
}
|
||||||
|
|
||||||
package var legalPositions: [Square.Position] {
|
internal var legalPositions: [Square.Position] {
|
||||||
return pseudoLegalPositions.filter { isLegal(pos: $0) }
|
return pseudoLegalPositions.filter { isLegal(on: $0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
package func move(dst: Square.Position) -> Bool {
|
internal func move(to dst: Square.Position) -> Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
package func isLegal(pos: Square.Position) -> Bool {
|
internal func isLegal(on pos: Square.Position) -> Bool {
|
||||||
|
if let board = board, let s = board[pos] {
|
||||||
|
if let p = s.piece {
|
||||||
|
if p.color == color { return false }
|
||||||
|
if p.kind == .King {
|
||||||
|
// TODO: Notify board of check
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
package init(color: Color, on position: Square.Position) {
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
internal init(color: Color, on position: Square.Position) {
|
||||||
self.color = color
|
self.color = color
|
||||||
self.position = position
|
self.position = position
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
public struct Square: Equatable {
|
public struct Square: Equatable {
|
||||||
|
|
||||||
public struct Position: Equatable {
|
public struct Position: Equatable {
|
||||||
public let file: UInt8
|
public let file: Int8
|
||||||
public let rank: UInt8
|
public let rank: Int8
|
||||||
|
|
||||||
public var index: Int {
|
public var index: Int {
|
||||||
let r = (8 - rank) % 8
|
return Int(8*(8-rank)+file-1)
|
||||||
return Int(8*r+file-1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func == (lhs: Position, rhs: Position) -> Bool {
|
public static func == (lhs: Position, rhs: Position) -> Bool {
|
||||||
@@ -15,8 +14,8 @@ public struct Square: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public let position: Position
|
public let position: Position
|
||||||
public let index: UInt8
|
public let index: Int
|
||||||
public var piece: Piece? = nil
|
var piece: Piece? = nil
|
||||||
public let color: Color
|
public let color: Color
|
||||||
|
|
||||||
public static func == (lhs: Square, rhs: Square) -> Bool {
|
public static func == (lhs: Square, rhs: Square) -> Bool {
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ import Engine
|
|||||||
let board = Board(fen: .init(fen: "rnbqkb1r/pppppppp/2n5/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"))
|
let board = Board(fen: .init(fen: "rnbqkb1r/pppppppp/2n5/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"))
|
||||||
// let board: Board = .init()
|
// let board: Board = .init()
|
||||||
print(board)
|
print(board)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user