cowd which runs

This commit is contained in:
Neale Pickett 2012-01-19 16:30:04 -07:00
parent 778e6b6641
commit c2fd1107c8
2 changed files with 226 additions and 36 deletions

View File

@ -0,0 +1,58 @@
The Cow Game
============
You are trying to guess a 4-nybble sequence. Each nybble will have
either 1 or 2 bits set, and the highest bit will never be set. The
game server will tell you how many nybbles in each guess were correct,
and how many had one correct bit. It does not tell you which
positions
The Cow Client
==============
The client connects to the Cow server running on the IPv6 address
provided in argument 1. If argument 2 is present, the client will
try to run it, providing stdin and stdout as in interactive mode.
In interactive mode (no argument 2), the client reads a guess in the
form of 4 ASCII numerals, and prints the number of correct nybbles
followed by the number of nybbles with one correct bit.
Here is an example of a session:
1111
12
2222
10
4444
02
4244
12
1244
22
1255
cow:xylep-radar-nanox
The Cow Protocol
================
cowd runs on port 3782.
The client always sends 6 octets. To request a new session, it sends
all zeroes. Otherwise it sends the 4-octet game identifier provided
by the server, concatenated with a 2-octet guess.
The server will respond with a new game identifier (4 octets) to a new
game request or if the game requested is too old. If a guess is
incorrect, the server will respond with either a 1-octet score in
which the high nybble is the number of correct nybbles in the guess,
and the low nybble is the number of nybbles in the guess with one
correct bit. If a guess is correct, the server will respond with a
token of length 5 octets or more.
There are multiple tokens, one per number of guesses used, up to
some maximum number of guesses defined per server instance.

View File

@ -1,27 +1,183 @@
#include <stdio.h> #include <stdio.h>
#include <sys/time.h> #include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <string.h>
#include <sysexits.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define TIMEOUT 30
#define NTOKENS 20
#define TOKENLEN 50
char tokens[NTOKENS][TOKENLEN];
int ntokens;
struct state {
time_t death;
uint16_t answer;
uint16_t guesses;
};
#define NSTATES 500
struct state states[NSTATES] = { 0 };
int
bind_port(struct in6_addr *addr, int fd, uint16_t port)
{
struct sockaddr_in6 saddr = { 0 };
saddr.sin6_family = AF_INET6;
saddr.sin6_port = htons(port);
memcpy(&saddr.sin6_addr, addr, sizeof *addr);
return bind(fd, (struct sockaddr *) &saddr, sizeof saddr);
}
struct newgame {
uint16_t offset;
uint16_t token;
};
void void
mungle(char *str, int len) new_game(int sock, time_t now, struct sockaddr_in6 *from,
socklen_t fromlen)
{ {
int i; int i;
struct newgame g;
for (i = 0; i < len; i += 1) { for (g.offset = 0; g.offset < NSTATES; g.offset += 1) {
str[i] ^= 0xff; struct state *s = &states[g.offset];
if (s->death < now) {
s->death = now + TIMEOUT;
s->guesses = 0;
s->answer = 0;
for (i = 0; i < 4; i += 1) {
s->answer = (s->answer << 4) | ((random() % 6) + 1);
}
break;
}
}
if (g.offset < NSTATES) {
sendto(sock, &g, sizeof(g), 0, (struct sockaddr *) from, fromlen);
}
}
struct guess {
uint16_t offset;
uint16_t token;
uint16_t guess;
};
void
loop(int sock)
{
struct guess g;
struct state *cur;
struct sockaddr_in6 from;
socklen_t fromlen = sizeof from;
time_t now = time(NULL);
/*
* Read guess
*/
{
ssize_t inlen;
inlen = recvfrom(sock, &g, sizeof g, 0,
(struct sockaddr *) &from, &fromlen);
if (inlen != sizeof g) {
return;
}
}
/*
* Bounds check
*/
if (g.offset >= NSTATES) {
g.offset = 0;
}
cur = &states[g.offset];
if ((g.token != cur->answer) || /* Wrong token? */
(cur->death < now) || /* Old game? */
(cur->guesses++ > 100)) { /* Too dumb? */
/*
* Start a new game
*/
new_game(sock, now, &from, fromlen);
return;
} else {
uint8_t reply;
int i;
for (i = 0; i < 4; i += 1) {
int s = (g.guess >> (i * 4)) & 0xf;
int a = (cur->answer >> (i * 4)) & 0xf;
if ((s < 1) || (s > 7)) {
reply = 0;
break;
} else if (s == a) {
reply += 0x10;
} else if (s & a) {
reply += 0x01;
}
}
if (reply == 0x40) {
if (cur->guesses > ntokens) {
sendto(sock, tokens[cur->guesses],
strlen(tokens[cur->guesses]), 0,
(struct sockaddr *) &from, fromlen);
}
} else {
sendto(sock, &reply, sizeof reply, 0, (struct sockaddr *) &from,
fromlen);
}
} }
} }
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
long answer = 0; long answer = 0;
int i; int sock;
int i;
struct in6_addr addr;
{ srand(time(NULL));
struct timeval tv;
gettimeofday(&tv, NULL); if (argc > 1) {
srandom(tv.tv_usec); if (0 >= inet_pton(AF_INET6, argv[1], &addr)) {
fprintf(stderr, "invalid address: %s\n", argv[1]);
return EX_IOERR;
}
} else {
memcpy(&addr, &in6addr_any, sizeof addr);
}
/*
* Read in tokens
*/
for (ntokens = 0; ntokens < NTOKENS; ntokens += 1) {
if (NULL == fgets(tokens[ntokens], TOKENLEN, stdin)) {
break;
}
}
printf("Read %d tokens.\n", ntokens);
/*
* Set up socket
*/
sock = socket(AF_INET6, SOCK_DGRAM, 0);
i = bind_port(&addr, sock, 3782);
if (-1 == i) {
perror("Bind port 3782");
return EX_IOERR;
} }
for (i = 0; i < 4; i += 1) { for (i = 0; i < 4; i += 1) {
@ -29,31 +185,7 @@ main(int argc, char *argv[])
} }
while (1) { while (1) {
char line[20]; loop(sock);
long guess;
int ret = 0;
if (NULL == fgets(line, sizeof(line), stdin)) {
break;
}
guess = strtol(line, NULL, 16);
for (i = 0; i < 4; i += 1) {
int g = (guess >> (i*4)) & 0xf;
int a = (answer >> (i*4)) & 0xf;
if ((g < 1) || (g > 7)) {
ret = 0;
break;
} else if (g == a) {
ret += 0x10;
} else if (g & a) {
ret += 0x01;
}
}
printf("%02x\n", ret);
} }
return 0; return 0;