-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add bit array for cell visibilities (#63)
- Loading branch information
Showing
6 changed files
with
351 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using System; | ||
|
||
namespace MPewsey.ManiaMap.Tests | ||
{ | ||
[TestClass] | ||
public class TestBitArray2D | ||
{ | ||
[TestMethod] | ||
public void TestEmptyInitializer() | ||
{ | ||
var array = new BitArray2D(); | ||
Assert.AreEqual(0, array.Rows); | ||
Assert.AreEqual(0, array.Columns); | ||
Assert.AreEqual(0, array.Array.Length); | ||
} | ||
|
||
[TestMethod] | ||
public void TestIndexSetterAndGetter() | ||
{ | ||
const int rows = 16; | ||
const int columns = 2; | ||
|
||
for (int i = 0; i < rows; i++) | ||
{ | ||
for (int j = 0; j < columns; j++) | ||
{ | ||
var value = 1 << (i * columns + j); | ||
var array = new BitArray2D(rows, columns); | ||
Assert.AreEqual(rows, array.Rows); | ||
Assert.AreEqual(columns, array.Columns); | ||
Assert.AreEqual(1, array.Array.Length); | ||
Assert.AreEqual(0, array.Array[0]); | ||
Assert.IsFalse(array[i, j]); | ||
array[i, j] = true; | ||
Assert.AreEqual(value, array.Array[0]); | ||
Assert.IsTrue(array[i, j]); | ||
array[i, j] = false; | ||
Assert.AreEqual(0, array.Array[0]); | ||
Assert.IsFalse(array[i, j]); | ||
} | ||
} | ||
} | ||
|
||
[TestMethod] | ||
public void TestToArrayString() | ||
{ | ||
var values = new int[,] | ||
{ | ||
{ 0, 1, 0, 0, 1, 1, 1, 0 }, | ||
{ 1, 0, 1, 1, 0, 0, 0, 1 }, | ||
{ 1, 1, 1, 1, 1, 1, 1, 1 }, | ||
{ 0, 1, 0, 1, 0, 1, 0, 1 }, | ||
{ 1, 0, 1, 0, 1, 0, 1, 0 }, | ||
}; | ||
|
||
var array = new BitArray2D(values.GetLength(0), values.GetLength(1)); | ||
Assert.AreEqual(2, array.Array.Length); | ||
|
||
for (int i = 0; i < values.GetLength(0); i++) | ||
{ | ||
for (int j = 0; j < values.GetLength(1); j++) | ||
{ | ||
array[i, j] = values[i, j] == 1; | ||
} | ||
} | ||
|
||
var str = array.ToArrayString(); | ||
Console.WriteLine(str); | ||
var expected = "[[01001110]\n [10110001]\n [11111111]\n [01010101]\n [10101010]]"; | ||
Assert.AreEqual(expected, str); | ||
} | ||
|
||
[TestMethod] | ||
public void TestClear() | ||
{ | ||
var array = new BitArray2D(2, 3); | ||
|
||
for (int i = 0; i < array.Rows; i++) | ||
{ | ||
for (int j = 0; j < array.Columns; j++) | ||
{ | ||
array[i, j] = true; | ||
} | ||
} | ||
|
||
array.Clear(); | ||
|
||
for (int i = 0; i < array.Rows; i++) | ||
{ | ||
for (int j = 0; j < array.Columns; j++) | ||
{ | ||
Assert.IsFalse(array[i, j]); | ||
} | ||
} | ||
} | ||
|
||
[TestMethod] | ||
public void TestGetOrDefault() | ||
{ | ||
var array = new BitArray2D(2, 3); | ||
array[1, 1] = true; | ||
Assert.IsTrue(array.GetOrDefault(-1, -1, true)); | ||
Assert.IsTrue(array.GetOrDefault(1, 1)); | ||
} | ||
|
||
[TestMethod] | ||
public void TestToString() | ||
{ | ||
var result = new BitArray2D(1, 2).ToString(); | ||
var expected = $"BitArray2D(Rows = 1, Columns = 2)"; | ||
Assert.AreEqual(expected, result); | ||
} | ||
|
||
[TestMethod] | ||
public void TestInitializeNegativeRow() | ||
{ | ||
Assert.ThrowsException<ArgumentException>(() => new BitArray2D(-1, 1)); | ||
} | ||
|
||
[TestMethod] | ||
public void TestInitializeNegativeColumn() | ||
{ | ||
Assert.ThrowsException<ArgumentException>(() => new BitArray2D(1, -1)); | ||
} | ||
|
||
[TestMethod] | ||
public void TestGetOutOfBoundsIndex() | ||
{ | ||
Assert.ThrowsException<IndexOutOfRangeException>(() => new BitArray2D()[-1, -1]); | ||
} | ||
|
||
[TestMethod] | ||
public void TestSetOutOfBoundsIndex() | ||
{ | ||
Assert.ThrowsException<IndexOutOfRangeException>(() => new BitArray2D()[-1, -1] = true); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
using System; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.Serialization; | ||
using System.Text; | ||
|
||
namespace MPewsey.ManiaMap | ||
{ | ||
/// <summary> | ||
/// An 2D array of bits. | ||
/// </summary> | ||
[DataContract] | ||
public class BitArray2D | ||
{ | ||
/// <summary> | ||
/// The number of bits in each chunk. | ||
/// </summary> | ||
public const int ChunkSize = 32; | ||
|
||
/// <summary> | ||
/// The number of rows in the array. | ||
/// </summary> | ||
[DataMember(Order = 1)] | ||
public int Rows { get; private set; } | ||
|
||
/// <summary> | ||
/// The number of columns in the array. | ||
/// </summary> | ||
[DataMember(Order = 2)] | ||
public int Columns { get; private set; } | ||
|
||
/// <summary> | ||
/// A flat array of data chunks. | ||
/// </summary> | ||
[DataMember(Order = 3)] | ||
public int[] Array { get; private set; } = System.Array.Empty<int>(); | ||
|
||
/// <summary> | ||
/// Accesses the bit at the specified index. | ||
/// </summary> | ||
/// <param name="row">The row index.</param> | ||
/// <param name="column">The column index.</param> | ||
/// <exception cref="IndexOutOfRangeException">Raised if the index is out of bounds.</exception> | ||
public bool this[int row, int column] | ||
{ | ||
get | ||
{ | ||
if (!IndexExists(row, column)) | ||
throw new IndexOutOfRangeException($"Index out of range: ({row}, {column})."); | ||
|
||
var index = Index(row, column); | ||
return (Array[index.X] & (1 << index.Y)) != 0; | ||
} | ||
set | ||
{ | ||
if (!IndexExists(row, column)) | ||
throw new IndexOutOfRangeException($"Index out of range: ({row}, {column})."); | ||
|
||
var index = Index(row, column); | ||
|
||
if (value) | ||
Array[index.X] |= 1 << index.Y; | ||
else | ||
Array[index.X] &= ~(1 << index.Y); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Initializes an empty array. | ||
/// </summary> | ||
public BitArray2D() | ||
{ | ||
|
||
} | ||
|
||
/// <summary> | ||
/// Initializes an array by size. | ||
/// </summary> | ||
/// <param name="rows">The number of rows in the array.</param> | ||
/// <param name="columns">The number of columns in the array.</param> | ||
/// <exception cref="ArgumentException">Raised if either the input rows or columns are negative.</exception> | ||
public BitArray2D(int rows, int columns) | ||
{ | ||
if (rows < 0) | ||
throw new ArgumentException($"Rows cannot be negative: {rows}."); | ||
if (columns < 0) | ||
throw new ArgumentException($"Columns cannot be negative: {columns}."); | ||
|
||
if (rows > 0 && columns > 0) | ||
{ | ||
Rows = rows; | ||
Columns = columns; | ||
Array = new int[(int)Math.Ceiling(rows * (double)columns / ChunkSize)]; | ||
} | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
return $"BitArray2D(Rows = {Rows}, Columns = {Columns})"; | ||
} | ||
|
||
/// <summary> | ||
/// Returns a string of all array elements. | ||
/// </summary> | ||
public string ToArrayString() | ||
{ | ||
var size = 2 + ChunkSize * Array.Length + 4 * Rows; | ||
var builder = new StringBuilder(size); | ||
builder.Append('['); | ||
|
||
for (int i = 0; i < Rows; i++) | ||
{ | ||
builder.Append('['); | ||
|
||
for (int j = 0; j < Columns; j++) | ||
{ | ||
if (this[i, j]) | ||
builder.Append('1'); | ||
else | ||
builder.Append('0'); | ||
} | ||
|
||
builder.Append(']'); | ||
|
||
if (i < Rows - 1) | ||
builder.Append("\n "); | ||
} | ||
|
||
builder.Append(']'); | ||
return builder.ToString(); | ||
} | ||
|
||
/// <summary> | ||
/// Clears all active bits in the array. | ||
/// </summary> | ||
public void Clear() | ||
{ | ||
System.Array.Clear(Array, 0, Array.Length); | ||
} | ||
|
||
/// <summary> | ||
/// Returns true if the index exists. | ||
/// </summary> | ||
/// <param name="row">The row.</param> | ||
/// <param name="column">The column.</param> | ||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public bool IndexExists(int row, int column) | ||
{ | ||
return (uint)row < Rows && (uint)column < Columns; | ||
} | ||
|
||
/// <summary> | ||
/// Returns a vector with the chunk index and position within the chunk | ||
/// for the specified row-column index. | ||
/// </summary> | ||
/// <param name="row">The row.</param> | ||
/// <param name="column">The column.</param> | ||
private Vector2DInt Index(int row, int column) | ||
{ | ||
var index = row * Columns + column; | ||
return new Vector2DInt(index / ChunkSize, index % ChunkSize); | ||
} | ||
|
||
/// <summary> | ||
/// Returns the value at the specified index if it exists. If not, | ||
/// returns the fallback value. | ||
/// </summary> | ||
/// <param name="row">The row index.</param> | ||
/// <param name="column">The column index.</param> | ||
/// <param name="fallback">The fallback value.</param> | ||
public bool GetOrDefault(int row, int column, bool fallback = false) | ||
{ | ||
if (IndexExists(row, column)) | ||
return this[row, column]; | ||
return fallback; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters