Files
EMBS/Practicals/practical-3b/game_of_life_practical3b/testbench.cpp

204 lines
6.6 KiB
C++

#include <stdio.h>
#include <stdbool.h>
#include "gameoflife.h"
// Convert 4 packed uint32 words into a 10x10 binary grid
void convert_words_to_grid(const uint32 words[4], int grid[10][10]) {
for (int i = 0; i < 100; i++) {
int word_index = i / 32;
int bit_index = i % 32;
int row = i / 10;
int col = i % 10;
grid[row][col] = (words[word_index] >> bit_index) & 1;
}
}
// Convert a 10x10 binary grid into 4 packed uint32 words
void convert_grid_to_words(const int grid[10][10], uint32 words[4]) {
for (int w = 0; w < 4; w++) {
words[w] = 0; // Clear buffer before OR-masking
}
for (int i = 0; i < 100; i++) {
int word_index = i / 32;
int bit_index = i % 32;
int row = i / 10;
int col = i % 10;
if (grid[row][col]) {
words[word_index] |= (1U << bit_index);
}
}
}
void print_grid(const char *label, const int grid[10][10]) {
printf("%s:\n", label);
for (int row = 0; row < 10; row++) {
for (int col = 0; col < 10; col++) {
printf("%c ", grid[row][col] ? '#' : '.');
}
printf("\n");
}
printf("\n");
}
bool run_test_case(const char *test_name, const int input[10][10], const int expected[10][10]) {
uint32 hw_in[4];
uint32 hw_out[4] = {0, 0, 0, 0};
int actual[10][10];
convert_grid_to_words(input, hw_in);
game_of_life_step(hw_in, hw_out);
convert_words_to_grid(hw_out, actual);
bool passed = true;
for (int row = 0; row < 10; row++) {
for (int col = 0; col < 10; col++) {
if (actual[row][col] != expected[row][col]) {
passed = false;
break;
}
}
if (!passed) break;
}
if (passed) {
printf("[PASS] %s\n", test_name);
} else {
printf("[FAIL] %s\n", test_name);
print_grid("Input", input);
print_grid("Expected", expected);
print_grid("Actual Output", actual);
}
return passed;
}
int main(void) {
bool all_passed = true;
// Test 1: Empty Grid (All 0s -> All 0s)
const int t1_input[10][10] = {0};
const int t1_expected[10][10] = {0};
all_passed &= run_test_case("Test 1: All Zero Grid", t1_input, t1_expected);
// Test 2: Still Life (2x2 Block & Tub)
// A 2x2 block has every cell with 3 neighbors; no outside cell has 3. It must remain identical.
const int t2_input[10][10] = {
{0,0,0,0,0,0,0,0,0,0},
{0,1,1,0,0,0,0,0,0,0},
{0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,1,0,1,0,0},
{0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0}
};
all_passed &= run_test_case("Test 2: Still Life (Block and Tub remain static)", t2_input, t2_input);
// Test 3: Oscillator (Blinker - Horizontal to Vertical)
// Three cells in a row (horizontal) oscillate to three cells in a column (vertical).
const int t3_input[10][10] = {
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,1,1,1,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0}
};
const int t3_expected[10][10] = {
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0}
};
all_passed &= run_test_case("Test 3: Oscillator (Blinker Horizontal -> Vertical)", t3_input, t3_expected);
// Test 4: Toroidal Seam / Wrap-around Test
// Place a Blinker horizontally straddling the right-to-left border: cells at [0][9], [0][0], [0][1].
// Under toroidal rules, this horizontal line should flip to a vertical line at:
// [9][0], [0][0], [1][0] (straddling the top-to-bottom border).
const int t4_input[10][10] = {
{1,1,0,0,0,0,0,0,0,1}, // row 0: cols 0, 1, and 9 are alive
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0}
};
const int t4_expected[10][10] = {
{1,0,0,0,0,0,0,0,0,0}, // cell at [0][0]
{1,0,0,0,0,0,0,0,0,0}, // cell at [1][0]
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,0,0} // cell at [9][0] (wrapped from top!)
};
all_passed &= run_test_case("Test 4: Toroidal Seam Wrap-Around (Blinker across boundaries)", t4_input, t4_expected);
// Test 5: Overpopulation Death (Dense 3x3 block)
// A solid 3x3 box of live cells. The center has 8 neighbors (dies).
// The 4 corners have 3 neighbors (survive).
// The 4 edges have 5 neighbors (die).
// The 4 adjacent external empty cells have 3 neighbors (birth).
// Resulting shape should be an empty diamond / cross outline.
const int t5_input[10][10] = {
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,1,1,1,0,0,0,0},
{0,0,0,1,1,1,0,0,0,0},
{0,0,0,1,1,1,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0}
};
const int t5_expected[10][10] = {
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0}, // Birth at [2][4]
{0,0,0,1,0,1,0,0,0,0}, // Corners survive, center edge dies
{0,0,1,0,0,0,1,0,0,0}, // Birth at [4][2] & [4][6], center [4][4] dies of overpopulation (8)
{0,0,0,1,0,1,0,0,0,0}, // Corners survive, center edge dies
{0,0,0,0,1,0,0,0,0,0}, // Birth at [6][4]
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0}
};
all_passed &= run_test_case("Test 5: Overpopulation & Birth (Solid 3x3 block)", t5_input, t5_expected);
// Final result
if (all_passed) {
printf("\n*************************************\n");
printf("ALL 5 UNIT TESTS PASSED SUCCESSFULLY! \n");
printf("*************************************\n");
return 0;
} else {
printf("\n[ERROR] One or more tests failed.\n");
return 1;
}
}