public void GenMove(Guid gameId, GoColor color, out GoMove newMove, out GoMoveResult result) { // Get a Fuego instance and start it up. FuegoInstance inst = null; try { inst = GetFuegoInstance(gameId, GoOperation.GenMove); inst.EnsureRunning(); inst.GenMove(color, out newMove, out result); } // ReSharper disable RedundantCatchClause catch (Exception) { throw; } // ReSharper restore RedundantCatchClause finally { if (inst != null) { inst.CurrentOperation = GoOperation.Idle; } } }
public void NotifyMovePlayed(GoColor color) { m_stopwatch.Stop(); // check the color TimeSettings toUse = color == GoColor.BLACK ? BlackTime : WhiteTime; if (toUse.MainTime.TotalSeconds <= 0) { toUse.Byoyomi -= m_stopwatch.Elapsed; --toUse.NumberOfMovesPerByoyomi; if (toUse.NumberOfMovesPerByoyomi <= 0) { // reset byoyomi time toUse.Byoyomi = OrginialTime.Byoyomi; toUse.NumberOfMovesPerByoyomi = OrginialTime.NumberOfMovesPerByoyomi; } } else { toUse.Byoyomi -= m_stopwatch.Elapsed; } m_stopwatch.Reset(); m_stopwatch.Start(); }
public GoMove Hint(Guid gameId, GoColor color) { // Get a Fuego instance and start it up. FuegoInstance inst = null; try { inst = GetFuegoInstance(gameId, GoOperation.Hint); inst.EnsureRunning(); var result = inst.Hint(color); return(result); } // ReSharper disable RedundantCatchClause catch (Exception) { throw; } // ReSharper restore RedundantCatchClause finally { if (inst != null) { inst.CurrentOperation = GoOperation.Idle; } } }
public void OnStonesCaptured(GoColor colorAttacker, int countCaptured) { m_turnDataCurr.piecesCaptured = countCaptured; m_scoreCaptures[(int)colorAttacker] += countCaptured; UpdateHud(); }
public GoMove Hint(GoColor color) { WriteCommand("reg_genmove", color == GoColor.Black ? "black" : "white"); string code, msg; ReadResponse(out code, out msg); GoMove hint; switch (msg) { case "PASS": hint = new GoMove(MoveType.Pass, color, null); break; case "resign": hint = new GoMove(MoveType.Resign, color, null); break; default: hint = new GoMove(MoveType.Normal, color, msg); break; } return(hint); }
public int piecesCaptured; // # of pieces captured by this play public GoTurn(int turnCurr, GoColor color) { turnNum = turnCurr; turnColor = color; piecesCaptured = 0; pointPlay = new Vector2(-1.0f, -1.0f); }
public List <GoPoint> WillCapture(GoMove move) { List <GoPoint> captured = new List <GoPoint>(); GoColor capturedColor = Helper.GetOppositeColor(move.Color); // check adjacents foreach (GoPoint potentialCaptured in getAdjacents(move.Point)) { if (Stones.ContainsKey(potentialCaptured)) { if (Stones[potentialCaptured] == capturedColor && !captured.Contains(potentialCaptured)) { // check liberties // note: this checking is done before the move is executed // means that actually if this adjacent group has only one liberty // we will play on it, so yes it will be captured List <GoPoint> group, liberties; CheckGroupLiberties(potentialCaptured, capturedColor, out group, out liberties); if (liberties.Count < 2) { // it is captured captured.AddRange(group); } } } } return(captured); }
// @param point Point to check if surrounded // @param groupColor Color of the group. Will check if surrounded by opposite color // @return The # of Stones captured public int TryCaptureGroup(Vector2 point, GoColor groupColor) { // Check: Bounds if (!IsValidPoint(point)) { return(0); } // Create hash to store all points that have been checked HashSet <GoPoint> hash_group = new HashSet <GoPoint>(); // Create queue to store list of points that need to be checked Queue <GoPoint> queue_points = new Queue <GoPoint>(); GoPoint point_curr; int count_captured = 0; int x_curr = (int)point.x; int y_curr = (int)point.y; // Retrieve initial point queue_points.Enqueue(gridPoints[x_curr, y_curr]); while (queue_points.Count > 0) { // Retrieve next point point_curr = queue_points.Dequeue(); x_curr = (int)point_curr.PointBoard.x; y_curr = (int)point_curr.PointBoard.y; bool b_allied_piece = false; // Only proceed if we haven't seen this point yet if (!hash_group.Contains(point_curr)) { // Check current point for an allied stone if (!point_curr.IsEmpty() && point_curr.GetStone().Color == groupColor) { b_allied_piece = true; } } if (b_allied_piece) { // Remove piece RemovePiece(point_curr.PointBoard); count_captured++; // Retrieve adjacent points if allied List <Vector2> list_adj = GetAdjacentPoints(point_curr.PointBoard); foreach (Vector2 point_adj in list_adj) { queue_points.Enqueue(gridPoints[(int)point_adj.x, (int)point_adj.y]); } } // Mark GoPoint as checked hash_group.Add(point_curr); } // Finally, return the # of Stones removed return(count_captured); }
public PlayerViewModel(GoPlayer p, GoColor color) { _color = color; _name = p.Name; _playerType = p.PlayerType; _level = p.Level; _komi = p.Komi; }
public static GoColor GetOppositeColor(GoColor color) { if (color == GoColor.EMPTY) { throw new Exception("how EMPTY is not a valid color to ask the opposite!"); } return color ^ GoColor.WHITE; }
public decimal ToFloat(GoColor color) { if (Winner != color) { return(-Score); } return(Score); }
public static GoColor GetOppositeColor(GoColor color) { if (color == GoColor.EMPTY) { throw new Exception("how EMPTY is not a valid color to ask the opposite!"); } return(color ^ GoColor.WHITE); }
public GameResultEventArgs(GoColor winner, float score, float blackScore, float whiteScore, bool timeOut) { WinnerColor = winner; Score = score; BlackScore = blackScore; WhiteScore = whiteScore; TimeOut = timeOut; }
public void Pass(GoColor color) { if (color == GoColor.EMPTY) { throw new Exception("You can't pass with an invalid color!"); } SendCommand(String.Format("play {0} PASS", color == GoColor.BLACK ? "B" : "W")); }
public PlayerViewModel(GoPlayer p, GoColor color) { _color = color; _name = p.Name; _playerType = p.PlayerType; _level = p.Level; _score = p.Score; }
public void GenerateMove(GoColor color, TimeSpan timeLeft, int numberOfByoyomiMove) { m_lastColorAsked = color; SendCommand(String.Format("time_left {0} {1} {2}" , color == GoColor.BLACK ? "b" : "w" , (int)timeLeft.TotalSeconds , numberOfByoyomiMove)); SendCommand(String.Format("genmove {0}", GetColorLetter(color))); }
// @return True if the specified color can play in the given Point public bool IsValidPlay(Vector2 point, GoColor colorStone) { int x = (int)point.x; int y = (int)point.y; // Check: If Point is empty. If not, we cannot play there if (!IsPointEmpty(point)) { Debug.Log("[GB] Could not create @ ( " + x + ", " + y + "); already occupied"); return(false); } // Check: If new Stone would be immediately be captured if (IsGroupCaptured(point, colorStone)) { // If a Stone would immediately be captured, we can play it // IFF that move would capture enemy Stone(s) // AND we aren't violating the rule of Ko List <Vector2> list_blocked = new List <Vector2>(); list_blocked.Add(point); // Check adjacent spots to see if we can capture enemy Stone(s) List <Vector2> list_adjacent = GetAdjacentPoints(point); bool b_capture_detected = false; foreach (Vector2 point_adj in list_adjacent) { GoPoint gp_adj = gridPoints[(int)point_adj.x, (int)point_adj.y]; // We only care about checking against enemy stones GoStone stone_adj = gp_adj.GetStone(); if (stone_adj.Color != colorStone) { b_capture_detected |= IsGroupCaptured(point_adj, stone_adj.Color, list_blocked); } } // If no captured were found, play is illegal if (!b_capture_detected) { Debug.Log("[GB] Could not create @ ( " + x + ", " + y + "); illegal move (surrounded)"); return(false); } // Check for Ko else { GoTurn turn_prev = gameStateManager.GetTurnPrev(); if (turn_prev.piecesCaptured == 1 && IsGroupCaptured(turn_prev.pointPlay, turn_prev.turnColor, list_blocked)) { Debug.Log("[GB] Could not create @ ( " + x + ", " + y + "); illegal move (Ko)"); return(false); } } } return(true); }
public void CheckGroupLiberties(GoPoint point, GoColor color, out List <GoPoint> group, out List <GoPoint> liberties) { group = new List <GoPoint>(); liberties = new List <GoPoint>(); if (color != GoColor.EMPTY) { CheckGroupLiberties_rec(point, color, ref group, ref liberties); } }
// Place piece on the board and handle board state management private void PlacePiece(GoStone piece, Vector2 pointGrid) { int point_x = (int)pointGrid.x; int point_y = (int)pointGrid.y; GoPoint point_center = gridPoints[point_x, point_y]; GoColor color_attacker = piece.Color; GoColor color_enemy = (color_attacker == GoColor.GC_Black ? GoColor.GC_White : GoColor.GC_Black); // Place GoPiece GameObject in the GoPoint point_center.SetPiece(piece); //// Decrement neighboring point's empty count //UpdateAdjacentEmptySpaces(pointGrid, (color_attacker == GoColor.GC_Black)); // Check if we have captured any enemy pieces (Up/Down/Left/Right Queue <Vector2> queue_adjacents = new Queue <Vector2>(); List <Vector2> list_adj = GetAdjacentPoints(pointGrid); foreach (Vector2 point_adj in list_adj) { queue_adjacents.Enqueue(point_adj); } int count_captured = 0; while (queue_adjacents.Count > 0) { // Retrieve Vector2 Vector2 vec_curr = queue_adjacents.Dequeue(); // Check if valid if (IsValidPoint(vec_curr)) { // Retrieve GoPoint GoPoint point_curr = gridPoints[(int)vec_curr.x, (int)vec_curr.y]; // Check if valid, if enemy color if (!point_curr.IsEmpty() && point_curr.GetStone().Color == color_enemy) { // If so, check if surrounded. if (IsGroupCaptured(vec_curr, color_enemy)) { // If so, remove group and report score to GoStateManager count_captured += TryCaptureGroup(vec_curr, color_enemy); } } } } // Report captured stones if (count_captured > 0) { gameStateManager.OnStonesCaptured(color_attacker, count_captured); } }
// get remaining byoyomi moves to play, 0 if not in byoyomi public int getByoyomiMove(GoColor color) { TimeSettings toUse = color == GoColor.BLACK ? BlackTime : WhiteTime; if (toUse.MainTime.TotalSeconds > 0) { return 0; } else { return toUse.NumberOfMovesPerByoyomi; } }
public int GetNumberOfCapturedStonesFor(GoColor color) { int count = 0; foreach (GoTurn turn in Turns) { if (turn.Move.Color == color && turn.Killed != null) { count += turn.Killed.Count; } } return(count); }
public TimeSpan getRemainingTime(GoColor color) { TimeSettings toUse = color == GoColor.BLACK ? BlackTime : WhiteTime; if (toUse.MainTime.TotalSeconds > 0) { return toUse.MainTime; } else { return toUse.Byoyomi.TotalMilliseconds > 0 ? toUse.Byoyomi : new TimeSpan(); } }
static Stone GetStone(GoColor color) { switch (color) { case GoColor.BLACK: return(Stone.Black); case GoColor.WHITE: return(Stone.White); case GoColor.EMPTY: return(Stone.Empty); } return(Stone.Empty); }
public TimeSpan getRemainingTime(GoColor color) { TimeSettings toUse = color == GoColor.BLACK ? BlackTime : WhiteTime; if (toUse.MainTime.TotalSeconds > 0) { return(toUse.MainTime); } else { return(toUse.Byoyomi.TotalMilliseconds > 0 ? toUse.Byoyomi : new TimeSpan()); } }
// get remaining byoyomi moves to play, 0 if not in byoyomi public int getByoyomiMove(GoColor color) { TimeSettings toUse = color == GoColor.BLACK ? BlackTime : WhiteTime; if (toUse.MainTime.TotalSeconds > 0) { return(0); } else { return(toUse.NumberOfMovesPerByoyomi); } }
public async Task <GoHintResponse> HintAsync(Guid gameid, GoColor color) { GoHintResponse rval = null; try { await Task.Factory.StartNew( () => { EnsureFuegoStarted().Wait(); _state.Operation = GoOperation.Hint; SaveState(); var result = ParseResponse(WriteCommand("reg_genmove", color == GoColor.Black ? "black" : "white")); GoMove hint; switch (result.Msg) { case "PASS": hint = new GoMove(MoveType.Pass, color, null); break; case "resign": hint = new GoMove(MoveType.Resign, color, null); break; default: hint = new GoMove(MoveType.Normal, color, result.Msg); break; } rval = new GoHintResponse(GoResultCode.Success, hint); _state.Operation = GoOperation.Idle; SaveState(); }); } catch (GoEngineException gex) { rval = new GoHintResponse(gex.Code, null); } catch (Exception ex) { rval = new GoHintResponse(GoResultCode.ServerInternalError, null); } Debug.Assert(rval != null, "rval != null"); return(rval); }
public void PlayMove(GoColor color, int x, int y) { int letter = (int)'A'; letter += x - 1; // because the I column doesn't exist if (x >= 9) { letter = letter + 1; } String posCoord = String.Format("{0}{1}", (char)letter, y); SendCommand(String.Format("play {0} {1}", GetColorLetter(color), posCoord)); }
public static string GetColorName(GoColor color) { switch( color) { case GoColor.BLACK: return "Black"; case GoColor.WHITE: return "White"; case GoColor.EMPTY: return "Empty"; default: throw new Exception("Unknow color"); } }
public GoGame(GoGameInfo info) { GameInfo = info; Clock = new GoClock(GameInfo.TimeSettings); Tree = new GoGameTree(); Board = new GoBoard(GameInfo.Size, GameInfo.Handicap); Turns = new List<GoTurn>(); KoPoint = null; IsGameOver = false; GameRule = RulesType.normal; NumberOfStonesToCapture = 5; m_currentTurn = 0; m_colorToMove = GameInfo.Handicap < 2 ? GoColor.BLACK : GoColor.WHITE; }
public GoGame(GoGameInfo info) { GameInfo = info; Clock = new GoClock(GameInfo.TimeSettings); Tree = new GoGameTree(); Board = new GoBoard(GameInfo.Size, GameInfo.Handicap); Turns = new List <GoTurn>(); KoPoint = null; IsGameOver = false; GameRule = RulesType.normal; NumberOfStonesToCapture = 5; m_currentTurn = 0; m_colorToMove = GameInfo.Handicap < 2 ? GoColor.BLACK : GoColor.WHITE; }
public static String GetColorLetter(GoColor color) { String colorLetter = ""; switch (color) { case GoColor.BLACK: colorLetter = "B"; break; case GoColor.WHITE: colorLetter = "W"; break; default: throw new Exception("Invalid color!"); } return(colorLetter); }
public static string GetColorName(GoColor color) { switch (color) { case GoColor.BLACK: return("Black"); case GoColor.WHITE: return("White"); case GoColor.EMPTY: return("Empty"); default: throw new Exception("Unknow color"); } }
public GoGameState(Guid gameId, byte size, GoPlayer player1, GoPlayer player2, GoGameStatus status, GoColor whoseTurn, string blackPositions, string whitePositions, List <GoMoveHistoryItem> goMoveHistory, decimal winMargin) { GameId = gameId; Size = size; Player1 = player1; Player2 = player2; Status = status; WhoseTurn = whoseTurn; BlackPositions = blackPositions; WhitePositions = whitePositions; GoMoveHistory = goMoveHistory; WinMargin = winMargin; }
// return true if the player run out of time! public bool update(GoColor color) { m_stopwatch.Stop(); bool colorCanLooseOnTime = CanLooseOnTime; // check the color if (color == GoColor.BLACK) { colorCanLooseOnTime &= !BlackInfiniteTime; } else { colorCanLooseOnTime &= !WhiteInfiniteTime; } TimeSettings toUse = color == GoColor.BLACK ? BlackTime : WhiteTime; TimeSpan remaingTime = toUse.MainTime; if (toUse.MainTime.TotalSeconds > 0) { toUse.MainTime -= m_stopwatch.Elapsed; remaingTime = toUse.MainTime; if (remaingTime.TotalMilliseconds < 0) { //toUse.Byoyomi += toUse.MainTime; // report what we put too much on main time toUse.Byoyomi = OrginialTime.Byoyomi; remaingTime = toUse.Byoyomi; } } else { toUse.Byoyomi -= m_stopwatch.Elapsed; remaingTime = toUse.Byoyomi; } m_stopwatch.Reset(); m_stopwatch.Start(); if (remaingTime.TotalMilliseconds >= -1990) // more time for pachi... { return(false); } return(colorCanLooseOnTime); }
private void SetPieces(string p, GoColor goColor) { var split = p.Split(' '); for (int index = 0; index < split.Length; index++) { var pos = split[index]; if (!String.IsNullOrWhiteSpace(pos) && Pieces.ContainsKey(pos)) { Pieces[pos].Color = goColor; } else if (!String.IsNullOrWhiteSpace(pos)) { // Can't set the sequence yet because index is not the correct value. Pieces.Add(pos, new PieceStateViewModel(pos, null, goColor, false, false, false)); } } }
public GoMove(GoColor color, GoPoint point, bool isPass) { Color = color; Point = point; IsPass = isPass; }
// return true if the player run out of time! public bool update(GoColor color) { m_stopwatch.Stop(); bool colorCanLooseOnTime = CanLooseOnTime; // check the color if (color == GoColor.BLACK) { colorCanLooseOnTime &= !BlackInfiniteTime; } else { colorCanLooseOnTime &= !WhiteInfiniteTime; } TimeSettings toUse = color == GoColor.BLACK ? BlackTime : WhiteTime; TimeSpan remaingTime = toUse.MainTime; if (toUse.MainTime.TotalSeconds > 0) { toUse.MainTime -= m_stopwatch.Elapsed; remaingTime = toUse.MainTime; if (remaingTime.TotalMilliseconds < 0) { //toUse.Byoyomi += toUse.MainTime; // report what we put too much on main time toUse.Byoyomi = OrginialTime.Byoyomi; remaingTime = toUse.Byoyomi; } } else { toUse.Byoyomi -= m_stopwatch.Elapsed; remaingTime = toUse.Byoyomi; } m_stopwatch.Reset(); m_stopwatch.Start(); if (remaingTime.TotalMilliseconds >= -1990) // more time for pachi... { return false; } return colorCanLooseOnTime; }
public void CheckGroupLiberties(GoPoint point, GoColor color, out List<GoPoint> group, out List<GoPoint> liberties) { group = new List<GoPoint>(); liberties = new List<GoPoint>(); if (color != GoColor.EMPTY) { CheckGroupLiberties_rec(point, color, ref group, ref liberties); } }
private void CheckGroupLiberties_rec(GoPoint point, GoColor color, ref List<GoPoint> alreadyParsed, ref List<GoPoint> liberties) { alreadyParsed.Add(point); foreach (GoPoint adjacent in getAdjacents(point)) { if( !alreadyParsed.Contains(adjacent)) { if (Stones.ContainsKey(adjacent)) { if (Stones[adjacent] == color) { CheckGroupLiberties_rec(adjacent, color, ref alreadyParsed, ref liberties); } } else if( !liberties.Contains(adjacent)) { liberties.Add(adjacent); } } } }
public void GenerateMove(GoColor color) { m_gtpEngine.GenerateMove(color , m_game.Clock.getRemainingTime(color) , m_game.Clock.getByoyomiMove(color)); }
public static String GetColorLetter(GoColor color) { String colorLetter = ""; switch (color) { case GoColor.BLACK: colorLetter = "B"; break; case GoColor.WHITE: colorLetter = "W"; break; default: throw new Exception("Invalid color!"); } return colorLetter; }
public int GetNumberOfCapturedStonesFor(GoColor color) { int count = 0; foreach (GoTurn turn in Turns) { if (turn.Move.Color == color && turn.Killed != null) { count += turn.Killed.Count; } } return count; }
// return true if the move is accepted, otherwise false // and message indicate why public bool PlayMove(GoMove move, ref GoMessageId message) { bool isValid = false; if (m_currentTurn != Turns.Count) { throw new Exception("You can't play if the state of the game is not the most recent, you might want to create a variation though but it is not implemented yet!"); } if(move.IsPass) { // this is a pass // is it the second one? if (Turns.Last().Move.Point == null) { // compute score float score = 0.0f; GoColor winner = GoColor.EMPTY; float whiteScore = 0.0f; float blackScore = 0.0f; StrasCouting(out whiteScore, out blackScore); if (whiteScore > blackScore) { winner = GoColor.WHITE; score = whiteScore - blackScore; } else { winner = GoColor.BLACK; score = blackScore - whiteScore; } // two pass the game is over switch (GameRule) { case RulesType.normal: OnGameIsOver(new GameResultEventArgs(winner, score, blackScore, whiteScore, false)); break; case RulesType.capture_N: int numberOfCapturesForWhite = GetNumberOfCapturedStonesFor(GoColor.WHITE); int numberOfCapturesForBlack = GetNumberOfCapturedStonesFor(GoColor.BLACK); GoColor moreCaptures = numberOfCapturesForWhite == numberOfCapturesForBlack ? GoColor.EMPTY : numberOfCapturesForWhite > numberOfCapturesForBlack ? GoColor.WHITE : GoColor.BLACK; OnGameIsOver(new GameResultEventArgs(moreCaptures , 0 , numberOfCapturesForBlack , numberOfCapturesForWhite , false)); break; default: throw new Exception("Error: unsupported rules type!"); } } GoTurn thisTurn = new GoTurn(move, m_currentTurn); thisTurn.OldKoPoint = KoPoint; // link the turns if (GetLastTurn() != null) { GetLastTurn().NextTurns.Add(thisTurn); thisTurn.PreviousTurn = GetLastTurn(); } // add to the list of turns Turns.Add(thisTurn); m_currentTurn++; isValid = true; } else if(move.Color == m_colorToMove) { // is it the ko point? if (KoPoint != null && move.Point == KoPoint) { message = GoMessageId.KO_THREAT_FIRST; isValid = false; } else { // is it on an empty space? if (Board.isPointFree(move.Point)) { // is it capturing something? List<GoPoint> captured = Board.WillCapture(move); if (captured.Count > 0) { GoTurn thisTurn = new GoTurn(move, m_currentTurn); thisTurn.Killed = captured; thisTurn.OldKoPoint = KoPoint; // link the turns if (GetLastTurn() != null) { GetLastTurn().NextTurns.Add(thisTurn); thisTurn.PreviousTurn = GetLastTurn(); } // add to the list of turns Turns.Add(thisTurn); m_currentTurn++; isValid = true; } else { // otherwise is it a suicide? if (Board.IsSuicide(move)) { message = GoMessageId.SUICIDE_MOVE; isValid = false; } else { // play the move increment turn counting GoTurn thisTurn = new GoTurn(move, m_currentTurn); thisTurn.OldKoPoint = KoPoint; // link the turns if (GetLastTurn() != null ) { GetLastTurn().NextTurns.Add(thisTurn); thisTurn.PreviousTurn = GetLastTurn(); } // add to the list of turns Turns.Add(thisTurn); m_currentTurn++; isValid = true; } } } else { message = GoMessageId.ALREADY_A_STONE; isValid = false; } } } else { // WARNING your move will be ignored message = GoMessageId.NOT_COLOR_TURN; isValid = false; } if (isValid) { Board.Execute(Turns.Last()); KoPoint = null; // did it created a ko? if (GetLastTurn().Killed != null && GetLastTurn().Killed.Count == 1) { // test if the captured position would be a suicide for the other color GoMove suicideTest = new GoMove( Helper.GetOppositeColor(move.Color), GetLastTurn().Killed[0], false); if( Board.IsSuicide(suicideTest)) { // but if it capture exactly one stone back it is a ko List<GoPoint> koTest = Board.WillCapture(suicideTest); if (koTest.Count == 1) { KoPoint = suicideTest.Point; } } } else { // otherwise reinitialise the ko to null KoPoint = null; } // if it capture and we are playing the capture 5 stones rules // we need to check if the game is over if (GameRule == RulesType.capture_N && GetLastTurn().Killed != null && GetLastTurn().Killed.Count > 0) { if (GetNumberOfCapturedStonesFor(m_colorToMove) >= NumberOfStonesToCapture) { OnGameIsOver(new GameResultEventArgs(m_colorToMove , 0 , GetNumberOfCapturedStonesFor(GoColor.BLACK) , GetNumberOfCapturedStonesFor(GoColor.WHITE) , false)); } } Clock.NotifyMovePlayed(m_colorToMove); m_colorToMove = Helper.GetOppositeColor(m_colorToMove); } return isValid; }