#include <stdio.h> #include <stdlib.h> #include <string.h> #include "tables.h" int lookup(char *target) { // Return index of the word. Binary chop for now. int low = 1, high = MAX_WORDS; // inclusive at each end while (low <= high) { int middle = (low+high)/2; int comparison = strcmp(word[middle].string, target); if (comparison < 0) { low = middle+1; } else if (comparison > 0) { high = middle-1; } else return middle; } return 0; // not found } /* look at best score for target_word. if score+this_score is lower than anything previously found, set up a backlink to this_word and update the recorded score with new lower score. We can then walk back from the target word to the root when we finally reach it. */ //#define INFINITY 99999 // very safe limit #define INFINITY 999 // safe for all practical purposes, failure would be pathological int level[MAX_WORDS+1]; // = INFINITY(*); int best_score[MAX_WORDS+1]; // = INFINITY(*); int backlink[MAX_WORDS+1]; // = 0(*); // level: depth of node in breadth-first search // score: cumulative score to here by best route // this_word: index of 'from' // target_word: index of 'to' // this_score: value of this play (1 for letterswap, 3 for letterbank) void push_link(int nextlevel, int score, int this_word, int target_word, int this_score) { int target_score = score + this_score; if (target_score < best_score[target_word]) { backlink[target_word] = this_word; best_score[target_word] = target_score; level[target_word] = nextlevel; } } void sweep(int this_level) { int each; int to_word; int lbptr, lsptr; for (each = 1; each <= MAX_WORDS; each++) { if (level[each] == this_level) { if ((lsptr = word[each].ls) != 0) { while ((to_word = ls[lsptr++]) != 0) { push_link(this_level+1, best_score[each], each, to_word, 1); } } if ((lbptr = word[each].lb) != 0) { while ((to_word = lb[lbptr++]) != 0) { push_link(this_level+1, best_score[each], each, to_word, 3); } } } } } int main(int argc, char **argv) { char *startword, *endword; int from_word, to_word, target_word; int lbptr, lsptr; int next_level; int i; if ((argc != 2) && (argc != 3)) { fprintf(stderr, "syntax: lb from_word [to_word]?\n\n"); exit(EXIT_FAILURE); } startword = argv[1]; if (argc == 3) endword = argv[2]; else endword = ""; for (i = 0; i <= MAX_WORDS; i++) { best_score[i] = INFINITY; backlink[i] = 0; level[i] = INFINITY; } from_word = lookup(startword); if (from_word == 0) { fprintf(stderr, "Start word \"%s\" not found in our allowed wordlist.\n", startword); exit(EXIT_FAILURE); } if (argc == 2) target_word = 0; else { target_word = lookup(endword); if (target_word == 0) { fprintf(stderr, "Target word \"%s\" not found in our allowed wordlist.\n", endword); exit(EXIT_FAILURE); } // This section is only for better error reporting, it can be deleted. lsptr = word[target_word].ls; lbptr = word[target_word].lb; if ((lsptr != 0) && (ls[lsptr] != 0) || (lbptr != 0) && (lb[lbptr] != 0)) { // found a link } else { // no links at all fprintf(stderr, "There is no word at all that can be linked to the target \"%s\"\n", endword); exit(EXIT_FAILURE); } } // initialise backlink[from_word] = 0; best_score[from_word] = 0; level[from_word] = 0; // another optional error report: lsptr = word[from_word].ls; lbptr = word[from_word].lb; if ((lsptr != 0) && (ls[lsptr] != 0) || (lbptr != 0) && (lb[lbptr] != 0)) { // found a link } else { // no links at all fprintf(stderr, "There is no word at all that the start word \"%s\" can link to\n", startword); exit(EXIT_FAILURE); } for (next_level = 0; next_level < INFINITY; next_level++) { sweep(next_level); // stop searching when we're at a level where it's just not possible // to generate a lower score than the best so far, eg if the score // is 14, there is no possible 15-step solution that could score 14 // or less, even if all moves are 1-point letterswaps. if (next_level > best_score[target_word]) break; } // check for a valid result: if (backlink[target_word] != 0) { int link = target_word; // walk backlinks and scores for (;;) { fprintf(stdout, "%s %d\n", word[link].string, best_score[link]); if (link == from_word) break; link = backlink[link]; } exit(EXIT_SUCCESS); } else { int i, best = 0, link; if (target_word != 0) fprintf(stderr, "Sorry, no path found from \"%s\" to \"%s\" in less than %d steps.\n", startword, endword, INFINITY); for (i = 1; i <= MAX_WORDS; i++) { if ((best_score[i] >= best) && (best_score[i] != INFINITY)) { best = best_score[i]; link = i; } } // walk backlinks and scores if (best != INFINITY) { fprintf(stderr, "Longest path seen was:\n"); for (;;) { fprintf(stdout, "%s %d\n", word[link].string, best_score[link]); if (link == from_word) break; link = backlink[link]; } } exit(EXIT_FAILURE); } return 0; }