// Copyright (c) 2019-2021 Jean-Philippe Bruyère // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Collections.Generic; using System.Diagnostics; using System.Numerics; using vke; namespace vkChess { public class ChessMoves : List { public void AddMove(string move) { if (!string.IsNullOrEmpty(move)) base.Add(move); } public void AddMove(string[] moves) { if (moves == null) return; if (moves.Length > 0) base.AddRange(moves); } } [DebuggerDisplay("{DebuggerDisplay}")] public class Piece { public static Vector4 blackColor = new Vector4(0.03f, 0.03f, 0.03f, 1f); public static Vector4 whiteColor = new Vector4(1.0f, 1.0f, 1.0f, 1f); public static HostBuffer instanceBuff; public static VkChess.InstanceData[] boardDatas; public static uint flushStart, flushEnd; static uint nextInstanceIdx; float xAngle, zAngle; Vector3 position, capturePos; public PieceType Type; public readonly ChessColor Player; public uint instanceIdx; public bool IsPromoted; public bool HasMoved; public bool Captured { get => BoardCell < 0; set { if (value) BoardCell = -1; else BoardCell = 0; } } public void SetCaptured (Vector3 capturePos) { Captured = true; HasMoved = true; this.capturePos = capturePos; } public int initX, initY; VkChess.InstanceData instData; public Piece (ChessColor player, PieceType type, int col, int line, Vector3 color) { this.instanceIdx = nextInstanceIdx++; this.Type = type; this.Player = player; initX = col; initY = line; position.X = initX; position.Y = initY; BoardCell = new Crow.Point (col, line); instData = new VkChess.InstanceData(new Vector4 (color, 1), Matrix4x4.Identity); if (player == ChessColor.Black && type == PieceType.Knight) zAngle = MathHelper.Pi; updatePos (); } public Crow.Point BoardCell; public Vector3 Position { get => position; set { if (value == position) return; position = value; updatePos(); } } public float X { get => position.X; set { if (position.X == value) return; position.X = value; updatePos(); } } public float Y { get => position.Y; set { if (position.Y == value) return; position.Y = value; updatePos(); } } public float Z { get => position.Z; set { if (position.Z == value) return; position.Z = value; updatePos(); } } public float XAngle { get => xAngle; set { if (xAngle == value) return; xAngle = value; updatePos(); } } public float ZAngle { get => zAngle; set { if (zAngle == value) return; zAngle = value; updatePos(); } } static Vector3 centerDiff = new Vector3(3.5f, 0, 3.5f); public void updatePos() { Quaternion q = Quaternion.CreateFromYawPitchRoll (zAngle, 0f, xAngle); Vector3 pos = (new Vector3 (position.X, position.Z, 7f-position.Y) - centerDiff) * 2; instData.mat = Matrix4x4.CreateFromQuaternion(q) * Matrix4x4.CreateTranslation(pos); UpdateBuff (instanceIdx, ref instData); } public static void UpdateBuff (uint instIdx, ref VkChess.InstanceData data) { if (instanceBuff == null) return; instanceBuff.Update (instIdx, data); if (flushEnd == 0) { flushStart = instIdx; flushEnd = instIdx + 1; } else if (instIdx < flushStart) flushStart = instIdx; else if (flushEnd <= instIdx) flushEnd = instIdx + 1; } public void SetColor (Vector3 newColor) { instData.color = new Vector4 (newColor, 1); UpdateBuff (instanceIdx, ref instData); } public static void UpdateCase (int x, int y, Vector4 colorDiff) { int index = y * 8 + x; boardDatas[index].color += colorDiff; UpdateBuff ((uint)index+32, ref boardDatas[index]); } public static void FlushHostBuffer () { if (flushEnd == 0) return; instanceBuff.Flush(flushStart, flushEnd); flushEnd = flushStart = 0; } public void Reset(bool animate = true) { xAngle = 0f; if (HasMoved) { if (animate) Animation.StartAnimation(new PathAnimation(this, "Position", new BezierPath( Position, new Vector3(initX, initY, 0f), Vector3.UnitZ))); } if (IsPromoted) Unpromote(); IsPromoted = false; HasMoved = false; BoardCell = new Crow.Point (initX, initY); } public void MoveTo (Crow.Point newPos, bool animate = false) { BoardCell = newPos; if (animate) { if (Captured) Animation.StartAnimation (new PathAnimation (this, "Position", new BezierPath ( Position, capturePos, Vector3.UnitZ), VkChess.animationSteps), 0); else if (BoardCell.X != Position.X || BoardCell.Y != Position.Y) Animation.StartAnimation (new PathAnimation (this, "Position", new BezierPath ( Position, new Vector3 (newPos.X, newPos.Y, 0f), Vector3.UnitZ), VkChess.animationSteps), 0); } } public void Promote(char prom, bool preview = false) { if (IsPromoted) throw new Exception("trying to promote already promoted " + Type.ToString()); if (Type != PieceType.Pawn) throw new Exception("trying to promote " + Type.ToString()); IsPromoted = true; switch (prom) { case 'q': Type = PieceType.Queen; break; case 'r': Type = PieceType.Rook; break; case 'b': Type = PieceType.Bishop; break; case 'k': Type = PieceType.Knight; break; case 'n': IsPromoted = false; return; default: throw new Exception("Unrecognized promotion"); } VkChess.updateInstanceCmds = true; } public void Unpromote() { IsPromoted = false; Type = PieceType.Pawn; VkChess.updateInstanceCmds = true; } string DebuggerDisplay => string.Format($"{Type}:{BoardCell.ToString()}"); } }