mirror of https://github.com/dirtbags/moth.git
cowd which runs
This commit is contained in:
parent
1d6b8f6971
commit
02f21d3964
|
@ -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.
|
|
@ -1,27 +1,183 @@
|
|||
#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
|
||||
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) {
|
||||
str[i] ^= 0xff;
|
||||
for (g.offset = 0; g.offset < NSTATES; g.offset += 1) {
|
||||
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
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
long answer = 0;
|
||||
int i;
|
||||
long answer = 0;
|
||||
int sock;
|
||||
int i;
|
||||
struct in6_addr addr;
|
||||
|
||||
{
|
||||
struct timeval tv;
|
||||
srand(time(NULL));
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
srandom(tv.tv_usec);
|
||||
if (argc > 1) {
|
||||
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) {
|
||||
|
@ -29,31 +185,7 @@ main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
while (1) {
|
||||
char line[20];
|
||||
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);
|
||||
loop(sock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue