/// <summary> /// /// </summary> /// <param name="gCase"></param> /// <param name="tdX">X component of trajectory in transformed space</param> /// <param name="tdY">Y component of trajectory in transformed space</param> /// <returns></returns> private static double[][] ValidPath(MirrorsCase gCase, int tdX, int tdY) { if (tdX == 0 && tdY == 0) { return(null); } // (rX,rY) are coordinates in real space double rX = gCase.MeX, rY = gCase.MeY; // (tX,tY) are coordinates in transformed space double tX = 0.5, tY = 0.5; // (rdX, rdY) are components of trajectory in real space int rdX = tdX, rdY = tdY; var resultList = new List <double[]>(); resultList.Add(new [] { rX, rY }); while (true) { //stepFactor * current trajectory takes us to the next cell edge (in both real and transformed space) double stepFactor = StepFactor(tX, tY, tdX, tdY); //Check whether travelling half way to cell edge exhausts budget if (D(tX + 0.5 * stepFactor * tdX, tY + 0.5 * stepFactor * tdY, 0.5, 0.5) > (gCase.D + epsilon)) { return(null); } //If travelling half way to cell edge hits Me, we are done if (D(rX + 0.5 * stepFactor * rdX, rY + 0.5 * stepFactor * rdY, gCase.MeX, gCase.MeY) < epsilon) { resultList.Add(new [] { gCase.MeX, gCase.MeY }); return(resultList.ToArray()); } tX += stepFactor * tdX; tY += stepFactor * tdY; rX += stepFactor * rdX; rY += stepFactor * rdY; resultList.Add(new[] { rX, rY }); //Check whether travelling all the way to cell edge exhausted budget if (D(tX, tY, 0.5, 0.5) > (gCase.D + epsilon)) { return(null); } ApplyReflection(gCase, rX, rY, ref rdX, ref rdY); } }
private static int SolveCase(MirrorsCase gCase) { //NB: Store valid paths in coprime form to avoid double-counting overlapping images var validPaths = new Dictionary <Tuple <int, int>, double[][]>(); var attemptedTrajectories = new Dictionary <Tuple <int, int>, bool>(); double[][] path = null; for (int i = -gCase.D; i <= gCase.D; i++) { for (int j = -gCase.D; j <= gCase.D; j++) { if ((i * i + j * j) <= gCase.D * gCase.D && !(i == 0 && j == 0)) { var reducedTrajectory = Reduce(i, j); if (attemptedTrajectories.ContainsKey(reducedTrajectory)) { continue; } attemptedTrajectories[reducedTrajectory] = true; if ((path = ValidPath(gCase, i, j)) != null) { validPaths[reducedTrajectory] = path; } } } } //int pathNo = 0; //foreach(var kvp in validPaths) //{ // Console.WriteLine(string.Format("#:{0}", pathNo++)); // foreach(var coords in kvp.Value) // Console.WriteLine(string.Format("{0},{1}", coords[0], coords[1])); //} return(validPaths.Count); }
private static MirrorsCase[] ReadFile(string filepath) { string[] lines = File.ReadAllLines(filepath); int lineIndex = 0; int cases = int.Parse(lines[lineIndex++]); var result = new MirrorsCase[cases]; for (int caseNo = 1; caseNo <= cases; caseNo++) { var items = lines[lineIndex++].Split(' ').Select(s => int.Parse(s)).ToArray(); int H, W, D; H = items[0]; W = items[1]; D = items[2]; var squares = new Sq[H][]; var meX = 0.0; var meY = 0.0; for (int y = 0; y < H; y++) { squares[y] = lines[lineIndex++].Select( (c, x) => c == '.' ? Sq.Empty : c == 'X' && ((meX = x + 0.5) + (meY = y + 0.5) <= int.MaxValue) ? Sq.Me : Sq.Mirror).ToArray(); } result[caseNo - 1] = new MirrorsCase { H = H, W = W, D = D, Squares = squares, MeX = meX, MeY = meY }; } return(result); }
private static void ApplyReflection(MirrorsCase gCase, double x, double y, ref int dx, ref int dy) { //(nx, ny) are the floor intersection to x + epsilon, y + epsilon int nx, ny; //(sx, sy) are the cell coordinates of the cell the ray is leaving int sx, sy; nx = (int)Math.Floor(x + epsilon); ny = (int)Math.Floor(y + epsilon); if (IsNearInteger(x) && dx > 0) { sx = nx - 1; } else { sx = nx; } if (IsNearInteger(y) && dy > 0) { sy = ny - 1; } else { sy = ny; } //Reorient so that the only relevant squares are E, NE and N var mirrorNeighbours = new[] { false, false, false }; int dxSign = dx >= 0 ? 1 : -1; int dySign = dy >= 0 ? 1 : -1; mirrorNeighbours[0] = (gCase.Squares[sy][sx + dxSign] == Sq.Mirror); mirrorNeighbours[1] = (gCase.Squares[sy + dySign][sx + dxSign] == Sq.Mirror); mirrorNeighbours[2] = (gCase.Squares[sy + dySign][sx] == Sq.Mirror); if (IsNearInteger(x) && !IsNearInteger(y)) { if (mirrorNeighbours[0]) { dx *= -1; } return; } if (!IsNearInteger(x) && IsNearInteger(y)) { if (mirrorNeighbours[2]) { dy *= -1; } return; } if (IsNearInteger(x) && IsNearInteger(y)) { if (mirrorNeighbours[0] && mirrorNeighbours[1]) { dx *= -1; } if (mirrorNeighbours[1] && mirrorNeighbours[2]) { dy *= -1; } if (mirrorNeighbours[1] && !mirrorNeighbours[0] && !mirrorNeighbours[2]) { dx = dy = 0; } return; } throw new Exception("Not on any square edge"); }