Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added take_an_l writeup #3

Merged
merged 5 commits into from
Sep 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions 2018/csaw_quals/take_an_l/README.md
Original file line number Diff line number Diff line change
@@ -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}
```
Binary file added 2018/csaw_quals/take_an_l/description.pdf
Binary file not shown.
12 changes: 12 additions & 0 deletions 2018/csaw_quals/take_an_l/takel.py
Original file line number Diff line number Diff line change
@@ -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'))
118 changes: 118 additions & 0 deletions 2018/csaw_quals/take_an_l/tile.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#include <stdio.h>
#include <stdlib.h>

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;
}