🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉 # unjoker's solution

## to Spiral Matrix in the C# Track

Published at Jan 30 2021 · 0 comments
Instructions
Test suite
Solution

Given the size, return a square matrix of numbers in spiral order.

The matrix should be filled with natural numbers, starting from 1 in the top-left corner, increasing in an inward, clockwise spiral order, like these examples:

###### Spiral matrix of size 3
``````1 2 3
8 9 4
7 6 5
``````
###### Spiral matrix of size 4
`````` 1  2  3 4
12 13 14 5
11 16 15 6
10  9  8 7
``````

## Running the tests

To run the tests, run the command `dotnet test` from within the exercise directory.

Initially, only the first test will be enabled. This is to encourage you to solve the exercise one step at a time. Once you get the first test passing, remove the `Skip` property from the next test and work on getting that test passing. Once none of the tests are skipped and they are all passing, you can submit your solution using `exercism submit SpiralMatrix.cs`

## Further information

For more detailed information about the C# track, including how to get help if you're having trouble, please visit the exercism.io C# language page.

## Source

Reddit r/dailyprogrammer challenge #320 [Easy] Spiral Ascension. https://www.reddit.com/r/dailyprogrammer/comments/6i60lr/20170619_challenge_320_easy_spiral_ascension/

### SpiralMatrixTests.cs

``````// This file was auto-generated based on version 1.1.0 of the canonical data.

using Xunit;

public class SpiralMatrixTests
{
[Fact]
public void Empty_spiral()
{
Assert.Empty(SpiralMatrix.GetMatrix(0));
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void Trivial_spiral()
{
var expected = new[,]
{
{ 1 }
};
Assert.Equal(expected, SpiralMatrix.GetMatrix(1));
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void Spiral_of_size_2()
{
var expected = new[,]
{
{ 1, 2 },
{ 4, 3 }
};
Assert.Equal(expected, SpiralMatrix.GetMatrix(2));
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void Spiral_of_size_3()
{
var expected = new[,]
{
{ 1, 2, 3 },
{ 8, 9, 4 },
{ 7, 6, 5 }
};
Assert.Equal(expected, SpiralMatrix.GetMatrix(3));
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void Spiral_of_size_4()
{
var expected = new[,]
{
{ 1, 2, 3, 4 },
{ 12, 13, 14, 5 },
{ 11, 16, 15, 6 },
{ 10, 9, 8, 7 }
};
Assert.Equal(expected, SpiralMatrix.GetMatrix(4));
}

[Fact(Skip = "Remove this Skip property to run this test")]
public void Spiral_of_size_5()
{
var expected = new[,]
{
{ 1, 2, 3, 4, 5 },
{ 16, 17, 18, 19, 6 },
{ 15, 24, 25, 20, 7 },
{ 14, 23, 22, 21, 8 },
{ 13, 12, 11, 10, 9 }
};
Assert.Equal(expected, SpiralMatrix.GetMatrix(5));
}
}``````
``````﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;

public class SpiralMatrix
{
//Here I'm using the metaphor of someone walking and turning around according
//to the instructions given by a Compass (the algorithm). Each step number is placed on the terrain.
//You can use different algorithms by changing the Compass implementation
public static int[,] GetMatrix(int size)
{
var terrain = new int[size,size];

var compass = new Compass(size);

new Explorer().ExploreTerrain(terrain, compass);

return terrain;
}

/// <summary>
/// This class encapsulates the algorithm used to determine when to turn.
/// Turns out there's a pattern to this. Starting from the last value, the distance between the turning points
/// increases every 2. The distance goes from 1 up to "size"
/// </summary>
class Compass
{
private Dictionary<int, bool> turningPoints;

public Compass(int sizeOfTerrainSide)
{
turningPoints = Enumerable.Range(1, terrainSize(sizeOfTerrainSide)).ToDictionary(i => i, i => false);
calculateTurningPoints(sizeOfTerrainSide);
}

public bool IsTurningPoint(int atStep) => turningPoints[atStep];

void calculateTurningPoints(int sizeOfTerrainSide)
{
var terrainToExplore = terrainSize(sizeOfTerrainSide);
for (int distance = 1; distance <= sizeOfTerrainSide; distance++)
{
terrainToExplore = registerTurningPoint(terrainToExplore, distance);
terrainToExplore = registerTurningPoint(terrainToExplore, distance);
}
}

int terrainSize(int sizeOfTerrainSide) => sizeOfTerrainSide * sizeOfTerrainSide;

int registerTurningPoint(int totalSteps, int distance)
{
totalSteps -= distance;
turningPoints[totalSteps] = true;
}
}

/// <summary>
/// This class encapsulates how to keep track of the steps (numbers to be placed)
/// </summary>
class Explorer
{
int _stepsTaken  = 1;
Position _currentPosition = new Position(0, 0);

{
_currentPosition.Forward();
_stepsTaken++;
}
void mapCurrentPosition(int[,] terrain) => terrain[_currentPosition.Y, _currentPosition.X] = _stepsTaken;

{
if(compass.IsTurningPoint(_stepsTaken))
_currentPosition.TurnRight();
}

public void ExploreTerrain(int[,] terrain, Compass compass)
{
while (_stepsTaken <= terrain.Length)
{
mapCurrentPosition(terrain);
}
}
}

/// <summary>
/// This class encapsulate how to navigate the terrain (Array)
/// </summary>
class Position
{
private Action _currentDirection;
public Position(int x, int y)
{
X = x;
Y = y;
_currentDirection = moveRight;
}
public int X { get; private set; }
public int Y { get; private set; }

void moveUp() => Y --;
void moveDown() => Y ++;
void moveLeft() => X --;
void moveRight() => X ++;

public void Forward() => _currentDirection.Invoke();

public void TurnRight()
{
if (_currentDirection == moveUp) _currentDirection = moveRight;
else if (_currentDirection == moveDown) _currentDirection = moveLeft;
else if (_currentDirection == moveRight) _currentDirection = moveDown;
else if (_currentDirection == moveLeft) _currentDirection = moveUp;
}

}

}``````