public bool Equals(Unit other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; if (!Equals(pivot, other.pivot)) return false; return members.Count == other.members.Count && members.All(m => other.members.Contains(m)); }
public IList<Tuple<Unit, IList<MoveType>>> AllPositions(Unit currentUnit) { if (!currentUnit.IsCorrect(map)) { return new Tuple<Unit, IList<MoveType>>[0]; } var checker = new ForbiddenSequenceChecker(currentUnit); var visited = new Dictionary<Unit, List<MoveType>> { { currentUnit, new List<MoveType>() }, }; var queue = new Queue<Unit>(new[] { currentUnit }); while (queue.Any()) { var unit = queue.Dequeue(); foreach (var move in allowedMoves.Where(m => checker.CheckLastMove(visited[unit], m))) { var next = unit.Move(move); if (!next.IsCorrect(map) || visited.ContainsKey(next)) continue; queue.Enqueue(next); visited[next] = visited[unit].Concat(new[] { move }).ToList(); } } return visited.Keys.Select(u => Tuple.Create(u, (IList<MoveType>)visited[u])).ToList(); }
public void LockUnit(Unit unit) { foreach (var cell in unit.members) { this[cell.x, cell.y].filled = true; } }
public Tuple<double, int> _Evaluate(Unit unit) { // Уничтожаемые линии это хорошо var dropped = DroppedLines(unit); double scoreDropped = Math.Pow(dropped.Count(isDrop => isDrop)-1, 2) + dropped.Select((isDrop, i) => isDrop ? 1.0 + ((double)i)/map.Height : 0.0).Sum(); var droppedLines = dropped.Count(isDrop => isDrop); // Занимаем полезные клетки - это хорошо var usabilities = unit.members.Select(m => UsabilityForCells[m]).ToArray(); double scoreOccupied = unit.members.Sum(m => UsabilityForCells[m]); // Ухудшаем возможность занять полезные клетки - это плохо var freeSurroundingCells = GetFreeSurroundingCells(unit); Dictionary<Cell, int> newFreeInputCells = freeSurroundingCells.ToDictionary(c => c, c => CountFreeInputs(c, unit)); double scoreClosed = freeSurroundingCells.Select(c => (newFreeInputCells[c] - freeInputsForCells[c])*UsabilityForCells[c]).Sum(); // Некомпактность - слишком много свободных клеток вокруг - это плохо double scoreCompact = ((double)-freeSurroundingCells.Length)/unit.GetSurroundingCells().Length; // Чем ниже тем лучше double scorePosHeigh = unit.members.Average(m =>((double)m.y)/map.Height); var score = 0.1*scoreDropped + scoreOccupied + 0.5*scoreClosed + 0.2*scoreCompact + 0.1*scorePosHeigh+ 0; return Tuple.Create(score, droppedLines); }
public void LongVTest() { var problem = new Problem { width = 6 }; var unit = new Unit { members = new HashSet<Cell>(new[] { new Cell { x = 0, y = 2 }, new Cell { x = 0, y = 3 }, new Cell { x = 1, y = 4 }, new Cell { x = 1, y = 3 }, new Cell { x = 2, y = 2 } }), pivot = new Cell { x = 1, y = 0 } }; var placedUnit = Game.SpawnUnit(unit, problem); var members = placedUnit.members.ToArray(); Assert.AreEqual(new Cell { x = 1, y = 0 }, members[0]); Assert.AreEqual(new Cell { x = 1, y = 1 }, members[1]); Assert.AreEqual(new Cell { x = 2, y = 2 }, members[2]); Assert.AreEqual(new Cell { x = 2, y = 1 }, members[3]); Assert.AreEqual(new Cell { x = 3, y = 0 }, members[4]); Assert.AreEqual(new Cell { x = 2, y = -2 }, placedUnit.pivot); }
public IEnumerable<Tuple<Unit, IList<MoveType>>> EndPositions(Unit currentUnit) { return AllPositions(currentUnit) .SelectMany(p => FinalMoves(p.Item1) .Select(f => Tuple.Create(p.Item1, (IList<MoveType>)p.Item2.Concat(new[] { f }) .ToList()))); }
public void Test(HashSet<Cell> unitCells, HashSet<Cell> surround) { Unit unit = new Unit(){members = unitCells, pivot = new Cell(){x=0,y=0}}; var surrounding = unit.GetSurroundingCells(); Assert.IsTrue(surround.Count == surrounding.Length); foreach (var surroundCell in surrounding) Assert.IsTrue(surround.Contains(surroundCell)); }
private static Tuple<double, Tuple<Unit, ReachablePositionsWithWords.VisitedInfo>[]> FindBestPositions_Recursive(int unitsAhead, Map map, Unit[] units, int unitIndex, string[] powerPhrases, bool[] spelledPhrases) { if (unitsAhead < 0 || unitIndex >= units.Length) { return Tuple.Create(0.0, new Tuple<Unit, ReachablePositionsWithWords.VisitedInfo>[0]); } var reachablePositions = new ReachablePositionsWithWords(map, powerPhrases, spelledPhrases); //var reachablePositions = new ReachablePositions(map); var evaluatePositions = new EvaluatePositions2(map); var unit = units[unitIndex]; var endPositions = reachablePositions.SingleEndPositions(unit); var estimated = new Dictionary<Unit, double>(); double? bestScore = null; var topOrderedPositions = endPositions .Select(p => { double value; if (estimated.TryGetValue(p.Item1, out value)) return Tuple.Create(value, p); var score = estimated[p.Item1] = evaluatePositions.Evaluate(p.Item1); return Tuple.Create(score, p); }) //.OrderByDescending(t => t.Item1) .OrderByDescending(t => (int)Math.Round(t.Item1 * 100)) .ThenByDescending(t => t.Item2.Item2.score) .TakeWhile(tt => { if (!bestScore.HasValue) { bestScore = tt.Item1; return true; } return Math.Abs(tt.Item1 - bestScore.Value) < 1e-6; }).ToList(); if (!topOrderedPositions.Any()) { return Tuple.Create(-1e3, new Tuple<Unit, ReachablePositionsWithWords.VisitedInfo>[0]); } var bestPosistions = Tuple.Create(double.MinValue, new Tuple<Unit, ReachablePositionsWithWords.VisitedInfo>[0]); foreach (var position in topOrderedPositions) { var newMap = map.Clone(); newMap.LockUnit(position.Item2.Item1); newMap.RemoveLines(); var nextPositions = FindBestPositions_Recursive(unitsAhead - 1, newMap, units, unitIndex + 1, powerPhrases, position.Item2.Item2.spelledWords); var score = position.Item1 + nextPositions.Item1; if (bestPosistions.Item1 < score) { bestPosistions = Tuple.Create(score, new[] {position.Item2}.Concat(nextPositions.Item2).ToArray()); } } return bestPosistions; }
public void SmallBacteriaTest() { var problem = new Problem { width = 10 }; var unit = new Unit { members = new HashSet<Cell>(new[] { new Cell { x = 1, y = 1 } }), pivot = new Cell { x = 2, y = 2 } }; var placedUnit = Game.SpawnUnit(unit, problem); Assert.AreEqual(new Cell { x = 4, y = 0 }, placedUnit.members.Single()); Assert.AreEqual(new Cell { x = 4, y = 1 }, placedUnit.pivot); }
public void SinglePointTest_5() { var problem = new Problem { width = 5 }; var unit = new Unit { members = new HashSet<Cell>(new[] { new Cell { x = 5, y = 5 } }), pivot = new Cell { x = 5, y = 5 } }; var placedUnit = Game.SpawnUnit(unit, problem); Assert.AreEqual(new Cell { x = 2, y = 0 }, placedUnit.members.Single()); Assert.AreEqual(new Cell { x = 2, y = 0 }, placedUnit.pivot); }
private int CountFreeInputs(Cell cell, Unit unit = null) { int nfree = 0; foreach (var move in up) { var c = cell.Move(move); if (c.x < 0 || c.y < 0 || c.x >= map.Width || c.y >= map.Height) continue; if (!map[c].filled && (unit == null || !unit.members.Contains(c))) nfree++; } return nfree; }
public void CountUnit() { var unit = new Unit {members = new HashSet<Cell>(new[] {new Cell {x = 1, y = 1}, new Cell {x = 2, y = 2}})}; Assert.AreEqual(2, ScoreCounter.GetMoveScore(unit, 0, 0)); Assert.AreEqual(102, ScoreCounter.GetMoveScore(unit, 1, 0)); Assert.AreEqual(302, ScoreCounter.GetMoveScore(unit, 2, 0)); Assert.AreEqual(602, ScoreCounter.GetMoveScore(unit, 3, 0)); Assert.AreEqual(102, ScoreCounter.GetMoveScore(unit, 1, 1)); Assert.AreEqual(112, ScoreCounter.GetMoveScore(unit, 1, 2)); Assert.AreEqual(362, ScoreCounter.GetMoveScore(unit, 2, 3)); }
private bool[] DroppedLines(Unit unit) { bool[] dropped = emptyCellsInLines.Select(c => false).ToArray(); var rect = unit.GetSurroundingRectangle(); for (int l = rect.Item1.y; l <= rect.Item2.y; ++l) { var emptyCellsInLine = emptyCellsInLines[l]; if (emptyCellsInLine.Count > unit.members.Count) continue; if (emptyCellsInLine.TrueForAll(c => unit.members.Contains(c))) dropped[l] = true; } return dropped; }
public double Evaluate(Unit unit) { var dropped = DroppedLines(unit); int[] freedomInts = CountFreeInputsForSurroundingPoints(unit); int[] byCountFree = new int[5]; foreach (var nfree in freedomInts) byCountFree[nfree]++; int scoreDropped = dropped.Select((isDrop, i) => isDrop ? 100+10*i : 0).Sum(); int scorePosHeigh = unit.GetSurroundingRectangle().Item1.y; var score = scoreDropped + scorePosHeigh //- 3*byCountFree[0] - 1*byCountFree[1] /* -2*byCountFree[2] - 1*byCountFree[3]*/ ; return score; }
public double Evaluate(Unit unit) { //Ценность занимаемых ячеек //Чем меньше остается пустых ячеек в строке - тем лучше //Чем меньше в данной стороке "глухих ячек" - тем лучше int score = 0; int nLinesDropped = 0; int[] occupied = new int[map.Height]; foreach (var occupiedCellsInLine in unit.members.GroupBy(m => m.y)) { var y = occupiedCellsInLine.Key; occupied[y] = occupiedCellsInLine.Count(); var newEmptyCells = emptyCells[y].Except(occupiedCellsInLine); int nEmpty = 0, nBlocked = 0; foreach (var c in newEmptyCells) if (CountFreeInputs(c, unit) == 0) nBlocked++; else nEmpty++; int lineScore = (int) (map.Width - nEmpty - nBlocked)*occupied[y]; if (nEmpty + nBlocked == 0) nLinesDropped++; if (lineScore > 0) score += lineScore; } score += map.Width*nLinesDropped*(nLinesDropped+1); // Насколько хорошая позиция юнита // Если рядом стена или заполненная ячейка - отлично // Если рядом пустая ячейка, то ее ценность - сколько у нее входов int surroundingScore = 0; foreach (var surroundingCellsInLine in unit.GetSurroundingCells().GroupBy(c => c.y)) { foreach (var cell in surroundingCellsInLine) { var cellScore = 0; if (!IsCorrect(cell) || map[cell].filled) cellScore= 10; else cellScore = 4*CountFreeInputs(cell, unit); if (cellScore == 0) cellScore= -(map.Width - emptyCells[cell.y].Length + occupied[cell.y] + (map.Width - emptyCells[cell.y].Length - blockedCells[cell.y] + occupied[cell.y] > map.Width*3/4 ? 10 : 0)); surroundingScore += cellScore; } } // Не находится ли юнит рядом с входом int posScore = 0; if (unit.members.Any(cell => (Math.Abs(cell.x - map.Width/2) < 3 && cell.y < 3))) if (nLinesDropped == 0) posScore = -2*map.Width; score = score + surroundingScore + posScore; return score; }
private int[] CountFreeInputsForSurroundingPoints(Unit unit) { List<int> countOfInputs = new List<int>(); foreach (var cell in unit.GetSurroundingCells()) if (cell.x < 0 || cell.y < 0 || cell.x >= map.Width || cell.y >= map.Height) continue; else if (map[cell].filled) continue; else { int nfree = 0; foreach (var move in up) { var c = cell.Move(move); if (c.x < 0 || c.y < 0 || c.x >= map.Width || c.y >= map.Height) continue; if (!map[c].filled && !unit.members.Contains(c)) nfree++; } countOfInputs.Add(nfree); } return countOfInputs.ToArray(); }
public double Evaluate(Unit unit) { return _Evaluate(unit).Item1; }
private Cell[] GetFreeSurroundingCells(Unit unit) { return unit.GetSurroundingCells().Where(c => freeInputsForCells.ContainsKey(c)).ToArray(); }
private int CountFreeInputs(Cell cell, Unit unit = null) { if (unit == null) return inputsOfCells[cell].Length; int nfree = 0; foreach (var c in inputsOfCells[cell]) if (unit.members.Contains(c)) continue; else nfree++; return nfree; }
public IList<Tuple<Unit, VisitedInfo>> EndPositions(Unit currentUnit) { return AllPositions(currentUnit) .SelectMany(p => FinalMoves(p.Item1).Select(f => Tuple.Create(p.Item1, new VisitedInfo { path = p.Item2.path.Concat(new[] { f }).ToList(), score = p.Item2.score, spelledWords = p.Item2.spelledWords }))).ToList(); }
public virtual void ReStart() { var filledCells = problem.filled.ToDictionary(cell => Tuple.Create<int, int>(cell.x, cell.y), cell => cell.Fill()); map = new Map(problem.width, problem.height); for (int x = 0; x < problem.width; x++) for (int y = 0; y < problem.height; y++) { Cell cell; if (filledCells.TryGetValue(Tuple.Create(x, y), out cell)) map[x, y] = cell; else map[x, y] = new Cell { x = x, y = y }; } units = problem.units; for (int i = 0; i < units.Length; i++) { units[i] = SpawnUnit(units[i], problem); } randomGenerator = new LinearCongruentalGenerator(seed); UnitIndeces = new int[problem.sourceLength]; for (int i = 0; i < problem.sourceLength; i++) { UnitIndeces[i] = randomGenerator.GetNext()%units.Length; } state = State.WaitUnit; step = 0; currentUnit = null; forbiddenSequenceChecker = null; moves = null; currentUnitIndex = 0; currentMovesScore = 0; currentSpellsScore = 0; previouslyExplodedLines = 0; enteredString = new StringBuilder(); EnteredMagicSpells = new List<string>(); }
public void DrawMap(Map map, Unit unit, bool locked = false) { for (int i = 0; i < 3 * map.Height + 1; i++) { var row = i / 3; if (i % 3 == 0) { console.BackgroundColor = ConsoleColor.Black; console.Write(" "); for (int col = 0; col < map.Width; ++col) { if (row % 2 == 1) { var viewInfo = GetViewInfo(map, unit, col, row - 1, locked); console.ForegroundColor = viewInfo.ForegroundColor; console.BackgroundColor = viewInfo.BackgroundColor; console.Write(new string(viewInfo.Char, 2)); } if (row < map.Height) { var viewInfo = GetViewInfo(map, unit, col, row, locked); console.ForegroundColor = viewInfo.ForegroundColor; console.BackgroundColor = viewInfo.BackgroundColor; console.Write(new string(viewInfo.Char, 2)); } else { console.BackgroundColor = ConsoleColor.Black; console.Write(" "); } if (row == 0) { console.BackgroundColor = ConsoleColor.Black; console.Write(" "); } else if (row % 2 == 0) { var viewInfo = GetViewInfo(map, unit, col, row - 1, locked); console.ForegroundColor = viewInfo.ForegroundColor; console.BackgroundColor = viewInfo.BackgroundColor; console.Write(new string(viewInfo.Char, 2)); } } } else { if (row % 2 == 1) { console.BackgroundColor = ConsoleColor.Black; console.Write(" "); } for (int col = 0; col < map.Width; ++col) { var viewInfo = GetViewInfo(map, unit, col, row, locked); console.ForegroundColor = viewInfo.ForegroundColor; console.BackgroundColor = viewInfo.BackgroundColor; console.Write(new string(viewInfo.Char, 4)); } } console.WriteLine(); } }
private CellViewInfo GetViewInfo(Map map, Unit unit, int col, int row, bool locked) { var result = GetMapViewInfo(map[col, row]); if (unit != null) { var unitCell = unit.members.SingleOrDefault(x => x.x == col && x.y == row); if (unitCell != null) { result = new CellViewInfo { BackgroundColor = locked ? ConsoleColor.Green : ConsoleColor.Red, Char = ' ' }; } var isPivot = unit.pivot.x == col && unit.pivot.y == row; if (isPivot) result.Char = 'X'; } return result; }
public void DrawUnit(Unit unit) { var minY = unit.members.Concat(new[]{unit.pivot}).Min(cell => cell.y); while (minY > 0) { unit = unit.Move(MoveType.NE); minY--; } while (minY < 0) { unit = unit.Move(MoveType.SE); minY++; } var minX = unit.members.Min(cell => cell.x); while (minX > 0) { unit = unit.Move(MoveType.W); minX--; } while (minX < 0) { unit = unit.Move(MoveType.E); minX++; } var map = new Map(unit.members.Concat(new[] { unit.pivot }).Max(cell => cell.x) + 1, unit.members.Concat(new[] { unit.pivot }).Max(cell => cell.y) + 1); console.WriteLine(); DrawMap(map, unit); }
public IList<Tuple<Unit, VisitedInfo>> AllPositions(Unit currentUnit) { if (!currentUnit.IsCorrect(map)) { return new Tuple<Unit, VisitedInfo>[0]; } var checker = new ForbiddenSequenceChecker(currentUnit); var visited = new Dictionary<Unit, VisitedInfo> { { currentUnit, new VisitedInfo { path = new List<MoveType>(), spelledWords = powerPhrasesSpelled } } }; var queue = new Queue<Unit>(new[] { currentUnit }); while (queue.Any()) { var unit = queue.Dequeue(); var currentInfo = visited[unit]; for (int wordIndex = 0; wordIndex < powerPhrases.Length; wordIndex++) { var powerPhrase = powerPhrases[wordIndex]; var next = unit; bool invalid = false; var moves = currentInfo.path.ToList(); foreach (var c in powerPhrase) { var move = MoveTypeExt.Convert(c).Value; if (!checker.CheckLastMove(moves, move)) { invalid = true; break; } next = next.Move(move); if (!next.IsCorrect(map) /*|| visited.ContainsKey(next)*/) { invalid = true; break; } moves.Add(move); } if (!invalid) { VisitedInfo prevInfo; var newScore = currentInfo.score; if (!currentInfo.spelledWords[wordIndex]) newScore += 300; newScore += 2 * powerPhrase.Length; if (!visited.TryGetValue(next, out prevInfo) || prevInfo.score < newScore) { queue.Enqueue(next); var newSpelledWords = new bool[currentInfo.spelledWords.Length]; Array.Copy(currentInfo.spelledWords, newSpelledWords, newSpelledWords.Length); newSpelledWords[wordIndex] = true; visited[next] = new VisitedInfo { path = moves, score = newScore, spelledWords = newSpelledWords }; } } } foreach (var move in allowedMoves.Where(m => checker.CheckLastMove(currentInfo.path, m))) { var next = unit.Move(move); if (!next.IsCorrect(map)) continue; VisitedInfo prevInfo; if (!visited.TryGetValue(next, out prevInfo) || prevInfo.score < currentInfo.score) { queue.Enqueue(next); visited[next] = new VisitedInfo { path = currentInfo.path.Concat(new[] { move }).ToList(), score = currentInfo.score, spelledWords = currentInfo.spelledWords }; } } } return visited.Keys.Select(u => Tuple.Create(u, visited[u])).ToList(); }
public IEnumerable<Tuple<Unit, VisitedInfo>> SingleEndPositions(Unit currentUnit) { return EndPositions(currentUnit).GroupBy(t => t.Item1).Select(g => g.First()); }
private Tuple<double, Tuple<Unit, ReachablePositionsWithWords.VisitedInfo>[]> FindBestPositions_Recursive(int unitsAhead, Map map, Unit[] units, int unitIndex, string[] powerPhrases, bool[] spelledPhrases) { if (unitsAhead < 0 || unitIndex >= units.Length) { return Tuple.Create(0.0, new Tuple<Unit, ReachablePositionsWithWords.VisitedInfo>[0]); } var reachablePositions = new ReachablePositionsWithWords(map, powerPhrases, spelledPhrases); //var reachablePositions = new ReachablePositions(map); // var evaluatePositions = new EvaluatePositions(map); var evaluatePositions = new EvaluatePositions2(map); var unit = units[unitIndex]; var endPositions = reachablePositions.SingleEndPositions(unit); var estimated = new Dictionary<Unit, double>(); var topOrderedPositions = endPositions .Select(p => { double value; if (estimated.TryGetValue(p.Item1, out value)) return Tuple.Create(value, p); var score = estimated[p.Item1] = evaluatePositions.Evaluate(p.Item1); return Tuple.Create(score, p); }) //.OrderByDescending(t => t.Item1) .OrderByDescending(t => (int)Math.Round(t.Item1 * 100) + t.Item2.Item2.score) .ToList(); var equallyTop = GetEquallyGoodTop(topOrderedPositions); var positionsForLookingAhead = topOrderedPositions.Take(Math.Max(equallyTop, minTopUnitCount)).ToList(); if (!positionsForLookingAhead.Any()) { return Tuple.Create(-1e3, new Tuple<Unit, ReachablePositionsWithWords.VisitedInfo>[0]); } var bestPosistions = Tuple.Create(double.MinValue, new Tuple<Unit, ReachablePositionsWithWords.VisitedInfo>[0]); foreach (var position in positionsForLookingAhead) { var newMap = map.Clone(); newMap.LockUnit(position.Item2.Item1); newMap.RemoveLines(); var nextPositions = FindBestPositions_Recursive(unitsAhead - 1, newMap, units, unitIndex + 1, powerPhrases, position.Item2.Item2.spelledWords); var score = position.Item1 + nextPositions.Item1; if (bestPosistions.Item1 < score) { bestPosistions = Tuple.Create(score, new[] {position.Item2}.Concat(nextPositions.Item2).ToArray()); } } return bestPosistions; }
public void Step() { ++step; switch (state) { case State.WaitUnit: if (currentUnitIndex++ >= problem.sourceLength) { state = State.End; return; } spawnedUnitIndex = UnitIndeces[currentUnitIndex - 1]; var spawnedUnit = units[spawnedUnitIndex]; if (!spawnedUnit.IsCorrect(map)) { state = State.End; return; } currentUnit = spawnedUnit; forbiddenSequenceChecker = new ForbiddenSequenceChecker(currentUnit); moves = new List<MoveType>(); state = State.UnitInGame; return; case State.UnitInGame: char move; if (!TryGetNextMove(out move)) { state = State.End; return; } var moveType = MoveTypeExt.Convert(move); if (moveType == null) { state = State.EndInvalidCommand; return; } enteredString.Append(move); ParseNewMagicSpells(); var movedUnit = currentUnit.Move(moveType.Value); if (!movedUnit.IsCorrect(map)) { LockUnit(currentUnit); currentUnit = null; state = State.WaitUnit; return; } if (!forbiddenSequenceChecker.CheckLastMove(moves, moveType.Value)) { state = State.EndPositionRepeated; return; } moves.Add(moveType.Value); currentUnit = movedUnit; return; case State.EndInvalidCommand: return; case State.End: return; default: throw new ArgumentOutOfRangeException(); } }
private List<MoveType> FinalMoves(Unit unit) { return allowedMoves.Where(m => !unit.Move(m).IsCorrect(map)).ToList(); }
private static int FirstInUnit(Cell[] cells, Unit unit) { int i = 0; foreach (var cell in cells) { if (unit.members.Contains(cell)) return i; ++i; } return i; }