twistturns

generates dot files of the graph of all reachable states in a Top Spin puzzle
git clone https://wehaveforgeathome.hates.computer/twistturns.git
Log | Files | Refs | LICENSE

utils.c (10885B)


      1 /***
      2 * utils.c
      3 * 
      4 * utility subroutines for tree
      5 *
      6 ***
      7 * version 2
      8 ***
      9 * print_states_dot() now only prints one of the two links,
     10 * using Jim's pointer trick.
     11 * now outputs colored links (red for rotation, red for step)
     12 ***/
     13 
     14 #include <stdio.h>
     15 #include <stdlib.h>
     16 #include "primes.h"
     17 #include "perm_gen.h"
     18 
     19 // type definitions used later
     20 typedef struct advance_node_type *advance_node;
     21 typedef struct state_type *state;
     22 
     23 // since the number of times you can advance the tiles is variable,
     24 // links to the states that result from those changes will go into an
     25 // array
     26 struct advance_node_type {
     27 	int steps; // how far to advance
     28 	state leads_to; // what state you enter after making that move
     29 	advance_node next;
     30 };
     31 
     32 // constructor for advance_node
     33 advance_node new_advance_node(int i, advance_node next, state leads_to) {
     34 	advance_node self = (advance_node) malloc(sizeof(struct advance_node_type));
     35 	if (self){                  // don't do this if memory wasn't allocated.
     36 		self->steps = i;      // set fields
     37 		self->next = next;
     38 		self->leads_to = NULL;
     39 	}
     40 	return self;
     41 }
     42 
     43 // 
     44 struct state_type {
     45 	link board;
     46 	state next;
     47 	state rotate;
     48 	advance_node advances;
     49 };
     50 
     51 // constructor for single node
     52 state new_state(link chain, state next) {
     53 	state self = (state) malloc(sizeof(struct state_type));
     54 	if (self){                  // don't do this if memory wasn't allocated.
     55 		self->board = chain;      // set fields
     56 		self->next = next;
     57 		self->rotate = NULL; // set later
     58 		self->advances = NULL; // set later
     59 	}
     60 	return self;
     61 }
     62 
     63 // utilities
     64 
     65 // compute the base signature of a chain of a given size
     66 // i.e. if the board is size 3, the base sig is
     67 // (first prime) * (second prime) * (3rd prime)
     68 // 2*3*5 = 30
     69 int base_signature(prime list) {
     70 	int product = 1;
     71 	while (list != NULL) {
     72 		product *= list->value;
     73 		list = list->next;
     74 	}
     75 	return product;
     76 }
     77 
     78 // compute the signature of a given chain
     79 // i.e. if the chain is (0,1,1):
     80 // sig = 2*3*3 = 18
     81 int gen_sig (link chain, prime list) {
     82 	link first_chain = chain;
     83 	prime first_prime = list;
     84 	int product = 1;
     85 	while (chain != NULL) {
     86 		int i = chain->value;
     87 		list = first_prime;
     88 		//printf("  for %d:\n", chain->value);
     89 		while((list != NULL) && (i > 0)) {
     90 			i--;
     91 			list = list->next;
     92 		}
     93 		//printf("    prime is %d\n", list->value);
     94 		product *= list->value;
     95 		chain = chain->next;
     96 	}
     97 	return product;
     98 }
     99 
    100 // given the base signature of chain of length n,
    101 // determine if a given chain contains duplicates
    102 int no_dupes(link chain, int sig, prime primes) {
    103 	if (gen_sig(chain, primes) != sig) {
    104 		return 0;
    105 	}
    106 	return 1;
    107 }
    108 
    109 // given a chain and a distance, advance chain until that position
    110 // fails for numbers higher than n-1, where n is the size of the chain
    111 link advance(link chain, int r) {
    112 	link old_start = chain; // pointer to first
    113 	// move to the rth node
    114 	while (r > 0) {
    115 		r--;
    116 		chain = chain->next;
    117 	}
    118 	link new_start = chain; // this is the new start of the chain
    119 	// move to the end of the chain
    120 	while (chain->next != NULL) {
    121 		chain = chain->next;
    122 	}
    123 	chain->next = old_start; // connect old start to end
    124 	// add the null to the end of old chain segment
    125 	while (chain->next != new_start) {
    126 		chain = chain->next;
    127 	}
    128 	chain->next = NULL;
    129 	return new_start;
    130 }
    131 
    132 // given a chain and a wheel radius, spin the first r elements of the chain
    133 link spin(link chain, int r) {
    134 	link old = chain;
    135 	// move to the rth node
    136 	while (r > 1) {
    137 		r--;
    138 		chain = chain->next;
    139 	}
    140 	link new_start = chain;
    141 	link broken = chain->next; // bit that is not spun
    142 	link end = broken;
    143 	// pull bits off start, add to end of broken
    144 	while (old->next != end) {
    145 		link tmp = old->next;
    146 		old->next = broken;
    147 		broken = old;
    148 		old = tmp;
    149 	}
    150 	new_start->next = broken; // reconnect broken bits to start
    151 	return new_start;
    152 }
    153 
    154 // print list of chains
    155 int print_states(state first) {
    156 	printf("  list of states:\n");
    157 	state tmp = first;
    158 	while (tmp != NULL) {
    159 		printf("  %p\n    board: ", tmp);
    160 		link links = tmp->board;
    161 		while (links != NULL) {
    162 			printf("%d ", links->value);
    163 			links = links->next;
    164 		}
    165 		printf("\n");
    166 		printf("    rotate: %p\n", tmp->rotate);
    167 		printf("    steps: ");
    168 		advance_node tmp_ad = tmp->advances;
    169 		while (tmp_ad != NULL) {
    170 			printf("%d->%p ", tmp_ad->steps, tmp_ad->leads_to);
    171 			tmp_ad = tmp_ad->next;
    172 		}
    173 		printf("\n");
    174 		tmp = tmp->next;
    175 	}
    176 	return 0;
    177 }
    178 
    179 // radix addition
    180 // returns a new chain
    181 link radix_increment(link target, int base) {
    182 	link new = chain_cpy(target);
    183 	link marker = new;
    184 	while (marker != NULL) {
    185 		if (marker->value < (base - 1)) {
    186 			marker->value++;
    187 			return new;
    188 		}
    189 		marker->value = 0;
    190 		marker = marker->next;
    191 	}
    192 	return NULL; // not big enough
    193 };
    194 
    195 // given n (number of links in chain), return list of possible advances
    196 advance_node n_advances(int n) {
    197 	n--;
    198 	advance_node marker = new_advance_node(n, NULL, NULL);
    199 	while (n > 1) {
    200 		n--;
    201 		advance_node new_node = new_advance_node(n, marker, NULL);
    202 		marker = new_node;
    203 	}
    204 	return marker;
    205 }
    206 
    207 // given the number of links, create a list of all the chain permutations with 
    208 // that many links
    209 state generate_perms(int n) {
    210 	//printf("  generating possible chains:\n");
    211 	// list of the first n primes
    212 	prime primes = first_n_primes(n);
    213 	// base signature
    214 	int base_sig = base_signature(primes);
    215 	//printf("    base signature: %d\n", base_sig);
    216 	// empty chain list
    217 	state states = new_state(NULL, NULL);
    218 	states->advances = n_advances(n);
    219 	// first one is lowest possible number with all linksn_
    220 	link lowest = new_chain(n);
    221 	link cur = lowest;
    222 	int c = n;
    223 	// fill in the first links
    224 	// i.e. 0 1 2 3
    225 	while (cur != NULL) {
    226 		c--;
    227 		cur->value = c;
    228 		cur = cur->next;
    229 	}
    230 	//printf("    lowest:   ");
    231 	//	print_chain(lowest);
    232 	//printf("\n");
    233 	// highest possible is reverse of lowest
    234 	int k = 0;
    235 	link highest = new_chain(n);
    236 	cur = highest;
    237 	while (cur != NULL) {
    238 		cur->value = k;
    239 		cur = cur->next;
    240 		k++;
    241 	}
    242 	//printf("    highest:  ");
    243 	//	print_chain(highest);
    244 	//printf("\n");
    245 	states->board = lowest; // first link in chain
    246 	state tmp_state = states; // generic marker version
    247 	link poss = chain_cpy(tmp_state->board);
    248 	//printf("    trying: (*success)\n");
    249 	while (!chains_equal(tmp_state->board, highest)) {
    250 		poss = radix_increment(poss, n);
    251 		//printf("      ");
    252 		//print_chain(poss);
    253 		if (no_dupes(poss, base_sig, primes)) {
    254 			state add_state = new_state(chain_cpy(poss), NULL);
    255 			add_state->advances = n_advances(n);
    256 			tmp_state->next = add_state;
    257 			tmp_state = add_state;
    258 			//printf("*");
    259 		}
    260 		//printf("\n");
    261 	} 
    262 	return states;
    263 }
    264 
    265 // given a list of states and a board, find the state with the board
    266 state board_search(link query, state states) {
    267 	state marker = states;
    268 	while (marker != NULL) {
    269 		if (chains_equal(query, marker->board)) {
    270 			return marker;
    271 		}
    272 		marker = marker->next;
    273 	}
    274 	return NULL;
    275 }
    276 
    277 // given a state, pair it with it's rotated state
    278 int link_rotations(state this_state, state states, int diameter) {
    279 	if (this_state->rotate != NULL) {
    280 		return 0;
    281 	}
    282 	link board = spin(chain_cpy(this_state->board), diameter);
    283 	state mate = board_search(board, states);
    284 	if (mate) {
    285 		this_state->rotate = mate;
    286 		mate->rotate = this_state;
    287 		return 1;
    288 	}
    289 	return 0;
    290 }
    291 
    292 // given a state and a step, pair it with it's complementary step
    293 int link_steps(state this_state, state states, advance_node adv, int n) {
    294 	if (adv->leads_to != NULL) {
    295 		return 0;
    296 	}
    297 	link board = advance(chain_cpy(this_state->board), adv->steps);
    298 	state mate = board_search(board, states);
    299 	if (mate) {
    300 		adv->leads_to = mate;
    301 		int i = n - adv->steps;
    302 		advance_node tmp_adv = mate->advances;
    303 		while (tmp_adv->steps != i) {
    304 			tmp_adv = tmp_adv->next;
    305 		}
    306 		tmp_adv->leads_to = this_state;
    307 		return 1;
    308 	}
    309 	return 0;
    310 }
    311 
    312 // given a number of links and the wheel diameter, 
    313 // create the list of permutations and link them
    314 state build_universe(int n, int d) {
    315 	state states = generate_perms(n);
    316 	state st_marker = states;
    317 	while (st_marker != NULL) {
    318 		link_rotations(st_marker, states, d);
    319 		advance_node adv_marker = st_marker->advances;
    320 		while (adv_marker->next != NULL) {
    321 			link_steps(st_marker, states, adv_marker, n);
    322 			adv_marker = adv_marker->next;
    323 		}
    324 		st_marker = st_marker->next;
    325 	}
    326 	return states;
    327 }
    328 
    329 // print board in dot-friend format
    330 int print_board_dot(link board) {
    331 	printf("\"");
    332 	print_chain(board);
    333 	printf("\"");
    334 	return 1;
    335 }
    336 
    337 // print states in dot format
    338 int print_states_dot(state states, int n) {
    339 	printf("graph T {\n");
    340 	state st_marker = states;
    341 	while (st_marker != NULL) {
    342 /***
    343 * if p1 < p2 
    344 * prevents duplicates
    345 * (Jim's idea)
    346 ***/	
    347 		// print rotate relationship
    348 		if (st_marker < st_marker->rotate) {
    349 			print_board_dot(st_marker->board);
    350 			printf(" -- ");
    351 			print_board_dot(st_marker->rotate->board);
    352 			printf(" [color=red];\n");
    353 		}
    354 		// print each advance relationship
    355 		advance_node ad_marker = st_marker->advances;
    356 		while (ad_marker != NULL) {
    357 			if (st_marker < ad_marker->leads_to) {
    358 				print_board_dot(st_marker->board);
    359 				printf(" -- ");
    360 				print_board_dot(ad_marker->leads_to->board);
    361 				printf(" [color=blue];\n");
    362 			}
    363 			ad_marker = ad_marker->next;
    364 		}
    365 		st_marker = st_marker->next;
    366 	}
    367 	printf("}\n");
    368 	return 0;
    369 }
    370 
    371 
    372 int test_utils() {
    373 	printf("***general utility tests\n");
    374 	prime test_prime = first_n_primes(3);
    375 	print_primes(test_prime);
    376 	int base = base_signature(test_prime);
    377 	printf("  base signature: %d\n", base);
    378 	link test_chain = new_chain(3);
    379 	test_chain->value = 0;
    380 	test_chain->next->value = 1;
    381 	test_chain->next->next->value = 2;
    382 	printf("  chain: ");
    383 	print_chain(test_chain);
    384 	printf("\n");
    385 	printf("  chain signature: %d\n", gen_sig(test_chain, test_prime));
    386 	printf("  no dupes? (expect 1): %d\n", no_dupes(test_chain, base, test_prime));
    387 	link test_chain2 = advance(test_chain, 2);
    388 	printf("  advance chain 2: ");
    389 	print_chain(test_chain2);
    390 	printf("\n");
    391 	link test_chain3 = spin(test_chain2, 2);
    392 	printf("  spin chain 2: ");
    393 	print_chain(test_chain3);
    394 	printf("\n");
    395 	test_chain3 = radix_increment(test_chain3, 3);
    396 	printf("  add 1 radix 3: ");
    397 	print_chain(test_chain3);
    398 	printf("\n");
    399 	test_chain3 = radix_increment(test_chain3, 3);
    400 	printf("  add 1 radix 3: ");
    401 	print_chain(test_chain3);
    402 	printf("\n");
    403 	test_chain3 = radix_increment(test_chain3, 3);
    404 	printf("  add 1 radix 3: ");
    405 	print_chain(test_chain3);
    406 	printf("\n");
    407 	test_chain3 = radix_increment(test_chain3, 3);
    408 	printf("  add 1 radix 3: ");
    409 	print_chain(test_chain3);
    410 	printf("\n");
    411 	////////////////////////////////
    412 	printf("***make the universe, pair some states\n");
    413 	state test_state = generate_perms(3);
    414 	link_rotations(test_state, test_state, 2);
    415 	link_steps(test_state->next, test_state, test_state->next->advances->next, 3);
    416 	print_states(test_state);
    417 	///////////////////////////////
    418 	printf("***make the universe, fill all states\n");
    419 	print_states(build_universe(3, 2));
    420 	printf("***output for dot\n");
    421 	print_states_dot(build_universe(3, 2),3);
    422 	return 0;
    423 }
    424