diff --git a/2018/csaw_quals/take_an_l/README.md b/2018/csaw_quals/take_an_l/README.md new file mode 100644 index 0000000..42a4a99 --- /dev/null +++ b/2018/csaw_quals/take_an_l/README.md @@ -0,0 +1,99 @@ +# Take an L + +## Challenge details +| Category | Points | +|:---------|-------:| +| misc | 200 | + +### Description +> Fill the grid with L's but avoid the marked spot for the W +> The origin is at (0,0) on the top left + +## Write-up + +`description.pdf` holds further details of the problem. +One important detail is that the grid is always `64 x 64` +and we have 5 seconds to send a response. + +The solution is simple enough to express recursively: +- base case: a `2 x 2` square with a mark is tiled in the obvious way +- inductive step: if a `2^k x 2^k` square with a mark can be tiled, + we tile a `2^(k + 1) x 2^(k + 1)` square by splitting it into four + `2^k x 2^k` quadrants and tiling an L with one cell in each + quadrant except the one with the mark. Consider the cells + used for the tiling marked. Now all quadrants are `2^k x 2^k` + and contain one mark, so they can be tiled recursively. + +For example: +``` +Start with +* * * * | * * * * +* * * * | * * * * +* * X * | * * * * +* * * * | * * * * +----------------- +* * * * | * * * * +* * * * | * * * * +* * * * | * * * * +* * * * | * * * * + +Tile an L: +* * * * | * * * * +* * * * | * * * * +* * X * | * * * * +* * * * | 1 * * * +----------------- +* * * 1 | 1 * * * +* * * * | * * * * +* * * * | * * * * +* * * * | * * * * + +Recurse: +* * | * * * * | * * +* * | * * * 2 | 2 * +--------- => --------- +* * | X * * 2 | X * +* * | * * * * | * * + +* * | * * * * | * * +* * | * * * 3 | 3 * +--------- => --------- +* * | * * * * | 3 * +1 * | * * 1 * | * * + +* * | * 1 * * | * 1 +* * | * * * 4 | * * +--------- => --------- +* * | * * * 4 | 4 * +* * | * * * * | * * + +1 * | * * 1 * | * * +* * | * * * * | 5 * +--------- => --------- +* * | * * * 5 | 5 * +* * | * * * * | * * + +Recurse again. +* * => 6 6 +* 2 => 6 2 + +* * => 7 7 +2 * => 2 7 + +* 2 => 8 2 +* * => 8 8 + +X * => X 9 +* * => 9 9 + +... and similarly for the other three 4x4 squares. +``` +The implementation is in `tile.c` and `takel.py`, invoked as: +```sh +$ gcc -O2 tile.c -o tile +$ python takel.py +``` +#### Flag +``` +flag{m@n_that_was_sup3r_hard_i_sh0uld_have_just_taken_the_L} +``` diff --git a/2018/csaw_quals/take_an_l/description.pdf b/2018/csaw_quals/take_an_l/description.pdf new file mode 100644 index 0000000..5664870 Binary files /dev/null and b/2018/csaw_quals/take_an_l/description.pdf differ diff --git a/2018/csaw_quals/take_an_l/takel.py b/2018/csaw_quals/take_an_l/takel.py new file mode 100644 index 0000000..c655d1a --- /dev/null +++ b/2018/csaw_quals/take_an_l/takel.py @@ -0,0 +1,12 @@ +from pwn import * +import subprocess + +r = remote('misc.chal.csaw.io', 9000) + +r.recvuntil('marked block: ') +blk = eval(r.recvline()) # we trust our csaw overlords + +res = subprocess.check_output(['./tile', str(blk[0]), str(blk[1])]) +r.send(res) + +print(r.recvall().rstrip().decode('utf8')) diff --git a/2018/csaw_quals/take_an_l/tile.c b/2018/csaw_quals/take_an_l/tile.c new file mode 100644 index 0000000..fbf2c70 --- /dev/null +++ b/2018/csaw_quals/take_an_l/tile.c @@ -0,0 +1,118 @@ +#include +#include + +enum { GRID_SIZE = 1 << 6 }; + +struct point { + int x, y; +}; + +int last = 0; +char seen[(GRID_SIZE * GRID_SIZE - 1) / 3] = {0}; +int array[GRID_SIZE][GRID_SIZE] = {{0}}; + +void print(void) { + for (int j = 0; j < GRID_SIZE; ++j) { + for (int i = 0; i < GRID_SIZE; ++i) { + int val = array[i][j]; + if (val != -1 && !seen[val]) { + seen[val] = 1; + printf("(%d, %d)", i, j); + if (array[i][j + 1] == val) + printf(", (%d, %d)", i, j + 1); + if (array[i + 1][j] == val) + printf(", (%d, %d)", i + 1, j); + if (array[i + 1][j + 1] == val) + printf(", (%d, %d)", i + 1, j + 1); + if (i > 0 && array[i - 1][j + 1] == val) + printf(", (%d, %d)", i - 1, j + 1); + printf("\n"); + } + } + } +} + +void tile(int sz, struct point mark, struct point cur) { + // 2x2, one is mark + // * * * X X * * * + // * X * * * * X * + // can only be tiled in one way + if (sz == 2) { + ++last; + for (int i = cur.x; i < (cur.x + 2); ++i) { + for (int j = cur.y; j < (cur.y + 2); ++j) { + if (!(i == mark.x && j == mark.y)) { + array[i][j] = last; + } + } + } + return; + } + + // middle and new marks: top right, top left, bottom right, bottom left + struct point mid, tr, tl, br, bl; + + mid.x = cur.x + sz / 2; + mid.y = cur.y + sz / 2; + ++last; + + // the quadrant that contains the old mark is untouched + // for the rest, tile an L with one cell in each quadrant + // and make these cells their marks + if (mark.x < mid.x && mark.y < mid.y) { + tl = mark; + tr = bl = br = mid; + --tr.x; + --bl.y; + array[tr.x][tr.y] = array[bl.x][bl.y] = array[br.x][br.y] = last; + } else if (mark.x < mid.x && mark.y >= mid.y) { + tr = mark; + tl = bl = br = mid; + --tl.x; + --tl.y; + --bl.y; + array[tl.x][tl.y] = array[bl.x][bl.y] = array[br.x][br.y] = last; + } else if (mark.x >= mid.x && mark.y < mid.y) { + bl = mark; + tl = tr = br = mid; + --tl.x; + --tl.y; + --tr.x; + array[tl.x][tl.y] = array[tr.x][tr.y] = array[br.x][br.y] = last; + } else if (mark.x >= mid.x && mark.y >= mid.y) { + br = mark; + tl = tr = bl = mid; + --tl.x; + --tl.y; + --tr.x; + --bl.y; + array[tl.x][tl.y] = array[tr.x][tr.y] = array[bl.x][bl.y] = last; + } + + tile(sz / 2, tl, cur); + tile(sz / 2, tr, (struct point){cur.x, mid.y}); + tile(sz / 2, bl, (struct point){mid.x, cur.y}); + tile(sz / 2, br, (struct point){mid.x, mid.y}); +} + +int main(int argc, char *argv[]) { + if (argc != 3) { + fprintf(stderr, "Usage: %s X Y\n", argv[0]); + return 1; + } + + int x = atoi(argv[1]); + int y = atoi(argv[2]); + last = 0; + + if (x >= GRID_SIZE || y >= GRID_SIZE) { + fprintf(stderr, "Invalid coords: %d %d\n", x, y); + return 2; + } + + array[x][y] = -1; + tile(GRID_SIZE, (struct point){x, y}, (struct point){0, 0}); + print(); + + return 0; +}