Hogwarts Wand Docs: ../server/fmtb_parse_oldA.c

Wand Sourcecode: ../server/fmtb_parse_oldA.c

//
// fmtb_parse_oldA.c - read a wpic*.txt file (stdin) and parse into motion codes (stdout)
//
// Copyright (C) 2006  Nathan (Acorn) Pooley
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License (gpl.txt) for more details. 
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
// 
// You can contact the author, Nathan (Acorn) Pooley, by writing
// to Nathan (Acorn) Pooley, 949 Buckeye Drive, Sunnyvale, CA  94086, USA
// or through the form at http://www.rawbw.com/~acorn/wand/feedback.html
//

//
//  NOTE: BASED ON fmtb_parse.c rev 1.3
//

//#@DOC@ read a wpic*.txt spell file (stdin) and parse into motion tokens

//
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>

#if 0
#include "tokens.h"
#endif

int OPT_quick = 0;
int OPT_silent = 0;

#define MAX_TOKENS  1000


#define TOKEN_CREATE(shape,dir,angle) \
    ((((dir)<0 || (shape)==SSHP_PAUSE)?0x80:0) | \
     (((angle)>>1) & 0xf) | \
     (((shape)&7) << 4))

#define TOKEN_SHAPE(tok)    (((tok)&0x70)>>4)
#define TOKEN_DIR(tok)      (((tok)&0x80)?-1:1)
#define TOKEN_ANGLE(tok)    (((tok)&0x0f)<<1)

typedef enum SpellShapeEnum {
    SSHP_PAUSE,
    SSHP_CIRCLE,
    SSHP_OCIRCLE,
    SSHP_OPULSE,
    SSHP_PULSE,
    SSHP_WILD,

    SSHP_CNT
} SpellShape;

typedef struct SpellRec {
    int   flags;
    int maxscore;
    int holdoff;
    char *name;
    char phonemes[30];
    unsigned char tokens[MAX_TOKENS];
    int len;
    int score;
} Spell;

typedef enum ShapeEnum {
    MSHP_PAUSE,
    MSHP_CLEAN_CIRCLE,
    MSHP_CLEAN_CIRCLE_PLUS,
    MSHP_DIRTY_CIRCLE,
    MSHP_DIRTY_CIRCLE_PLUS,
    MSHP_PULSE,
    MSHP_PULSE_PLUS,
    MSHP_CIRCLE_GAP,
    MSHP_CNT
} Shape;
static char *motionShapeStr[] = {
    "Pause    ",
    "Cln Circ ",
    "Cln Circ+",
    "Drt Circ ",
    "Drt Circ+",
    "Pulse    ",
    "Pulse+   ",
    "CircleGap",
};
static char *quadStr[] = {
    "Down ",
    "Right",
    "Up   ",
    "Left ",
};

static char *octStr[] = {
    "d ",
    "dr",
    " r",
    "ur",
    "u ",
    "ul",
    " l",
    "dl",
};

static char *octStr2[] = {
    "v",
    ".",
    ">",
    "'",
    "^",
    "`",
    "<",
    ",",
};

extern Spell spells[];


int OPT_quiet = 1;
int OPT_hex = 1;

#define DEBUG   0

#define CNT_MAX 1024


#define ABS(a)   ((a)<0?-(a):(a))
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))


#define TH_T_STILL      0.25
#define TH_A_JUMP       5
#define TH_A_NEWQUAD    7
#define TH_A_SNAPQUAD   6
#define TH_A_CONNECT    2

typedef enum MotionFlagsEnum {
    MFLG_ZERO           =   0x01,
    MFLG_MONO           =   0x02,
    MFLG_CIRCLE         =   0x04,
    MFLG_BEST_MAG       =   0x08,
    MFLG_BEST_IMP       =   0x10,
    MFLG_MISSING_CIRCLE =   0x20,

    MFLG_END            =   0xff
} MotionFlags;

typedef struct MotionRec {
    int     b_atime;
    int     b_dtime;
    int     b_imp;
    int     b_minmag;
    int     b_avgmag;
    int     b_maxmag;
    int     b_deltamag;
    int     b_angle;
    int     b_flags;

    struct MotionRec *c_next;
    struct MotionRec *c_prev;
    int c_shape;
    int c_time;
    int c_cnt;
    int c_imp;
    int c_isum;
    int c_dir;
    int c_maxstep;
    int c_oct;
    int c_best_angle;
    int c_best_mag;
    int c_flags;
    int c_maxdmag;
    int c_asum;


#if 0
    struct MotionRec *cc_next;
    int cc_shape;
    int cc_best_angle;
    int cc_imp;
#endif

    int token;
} Motion;

#define PAUSE 0x80

Spell spells[] = {
#include "spells.c"
    {0},
};

int ignoreSpells[8] = {
        100,    // SSHP_PAUSE
        15,     // SSHP_CIRCLE
        0,      // SSHP_OCIRCLE
        1,      // SSHP_OPULSE
        100,    // SSHP_PULSE
        0,      // SSHP_WILD
        99999,
        99999,
};

int ignoreMotions[8] = {
        0,      // MSHP_PAUSE,
        1,      // MSHP_CLEAN_CIRCLE,
        100,    // MSHP_CLEAN_CIRCLE_PLUS,
        1,      // MSHP_DIRTY_CIRCLE,
        20,     // MSHP_DIRTY_CIRCLE_PLUS,
        1,      // MSHP_PULSE,
        100,    // MSHP_PULSE_PLUS,
        0,      // MSHP_CIRCLE_GAP,
};

int scorePause[8] = {
        0,      // MSHP_PAUSE,
        30,     // MSHP_CLEAN_CIRCLE,
        1000,   // MSHP_CLEAN_CIRCLE_PLUS,
        30,     // MSHP_DIRTY_CIRCLE,
        1000,   // MSHP_DIRTY_CIRCLE_PLUS,
        30,     // MSHP_PULSE,
        1000,   // MSHP_PULSE_PLUS,
        0,      // MSHP_CIRCLE_GAP
};

int scorePulse[10] = {
        0,      // same dir
        0,      // off by 1
        0,      // off by 2
        0,      // off by 3
        0,      // off by 4 (45 deg)
        2,      // off by 5
        6,      // off by 6
        20,     // off by 7
        1000,   // off by 8 (90 deg)
        1000,   // off by 9
};


//===========================================================================
// fatalError()
//===========================================================================
void fatalError(const char *fmt, ...)
{
    va_list ap;
    fprintf(stderr,"\n\nERROR:\n");
    va_start(ap,fmt);
    vfprintf(stderr,fmt,ap);
    va_end(ap);
    fprintf(stderr,"\n\n");
    exit(1);
}

//===========================================================================
// adiff() - returned signed difference of 2 angles
//===========================================================================
int adiff(int a1, int a0)
{
    int a = a1 - a0;
    a &= 0x1f;
    if (a & 0x10) a -= 0x20;
    return a;
}

//===========================================================================
// odiff() - returned signed difference of 2 octants
//===========================================================================
int odiff(int o1, int o0)
{
    int o = o1 - o0;
    o &= 0x7;
    if (o & 0x4) o -= 0x8;
    return o;
}

//===========================================================================
// a2quad() - convert angle to quadrant
//===========================================================================
int a2quad(int a)
{
    return ((a+4) >> 3) & 3;
}
//===========================================================================
// quad2a() - convert quadrant to angle
//===========================================================================
int quad2a(int q)
{
    return (q<<3) & 0x1f;
}

//===========================================================================
// a2oct() - convert angle to octant
//===========================================================================
int a2oct(int a)
{
    return ((a+2) >> 2) & 7;
}

//===========================================================================
// oct2a() - convert octant to angle
//===========================================================================
int oct2a(int o)
{
    return (o<<2) & 0x1f;
}

//===========================================================================
// spellTokenStr()
//===========================================================================
char *spellTokenStr(int tok)
{
    static char buf[10];
    int oct = a2oct(TOKEN_ANGLE(tok));
    int dir = TOKEN_DIR(tok);
    switch(TOKEN_SHAPE(tok)) {
        case SSHP_PAUSE:   sprintf(buf,"Pause"); break;
        case SSHP_CIRCLE:  sprintf(buf," %3s%s",dir<0?" CW":"CCW",octStr2[oct]); break;
        case SSHP_OCIRCLE: sprintf(buf," %3s%s",dir<0?" cw":"ccw",octStr2[oct]); break;
        case SSHP_OPULSE:  sprintf(buf,"  op%s",octStr2[oct]); break;
        case SSHP_PULSE:   sprintf(buf,"   P%s",octStr2[oct]); break;
        case SSHP_WILD:    sprintf(buf,"Wild "); break;
        default:           sprintf(buf,"?????"); break;
    }
    return buf;
}

//===========================================================================
// motionTokenStr()
//===========================================================================
char *motionTokenStr(unsigned int tok)
{
    static char buf[10];
    int oct = a2oct(TOKEN_ANGLE(tok));
    int dir = TOKEN_DIR(tok);
    switch(TOKEN_SHAPE(tok)) {
        case MSHP_PAUSE:             sprintf(buf,"PAUSE"); break;
        case MSHP_CLEAN_CIRCLE:      sprintf(buf," %3s%s",dir<0?" CW":"CCW",octStr2[oct]); break;
        case MSHP_CLEAN_CIRCLE_PLUS: sprintf(buf,"+%3s%s",dir<0?" CW":"CCW",octStr2[oct]); break;
        case MSHP_DIRTY_CIRCLE:      sprintf(buf," %3s%s",dir<0?" cw":"ccw",octStr2[oct]); break;
        case MSHP_DIRTY_CIRCLE_PLUS: sprintf(buf,"+%3s%s",dir<0?" cw":"ccw",octStr2[oct]); break;
        case MSHP_PULSE:             sprintf(buf,"   p%s",octStr2[oct]); break;
        case MSHP_PULSE_PLUS:        sprintf(buf,"  +P%s",octStr2[oct]); break;
        case MSHP_CIRCLE_GAP:        sprintf(buf,"G%3s%s",dir<0?" cw":"ccw",octStr2[oct]); break;
        default:                      sprintf(buf,"?????"); break;
    }
    return buf;
}

//===========================================================================
// flagStr()
//===========================================================================
const char *flagStr(int f)
{
    static char s[10];

    s[0] = f & MFLG_ZERO     ? 'Z' : ' ';
    s[1] = f & MFLG_MONO     ? 'M' : ' ';
    s[2] = f & MFLG_CIRCLE   ? 'C' : ' ';
    s[3] = f & MFLG_BEST_MAG ? 'G' : ' ';
    s[4] = f & MFLG_BEST_IMP ? 'I' : ' ';
    s[5] = 0;
    return s;
}

//===========================================================================
// lookPrint()
//===========================================================================
void lookPrint(Motion *mlist, int mcnt)
{
    Motion *m, *mp;
    if (OPT_silent) return;
    mp = 0;
#if 0
    for (m=mlist; m; mp=m, m = m->c_next) {
        if (m->b_angle == 0xff) {
            printf(" 0x%08x: %04x Pause\n",
                m->b_atime,
                m->c_time);
        } else {
            printf(" 0x%08x: %04x shape=%d %s a=%02x %c %d%s %s d=%2d imp=%04x %04x MaxDMag=%02x maxStep=%02x "
                "ad=%3d flg=%02x %s  %02x  %s\n",
                m->b_atime,
                m->c_time,
                m->c_shape,
                motionShapeStr[m->c_shape],
                m->c_best_angle,
                quadStr[a2quad(m->c_best_angle)][0],
                m->c_oct,
                octStr[m->c_oct],
                octStr2[m->c_oct],
                m->c_dir,
                m->c_imp,
                m->c_isum,
                m->c_maxdmag,
                m->c_maxstep,
                (m==mlist || m->b_angle == 0xff || mp->b_angle == 0xff) ? 
                        0:adiff(m->c_best_angle,mp->c_best_angle),
                m->c_flags,
                flagStr(m->c_flags),
                m->token,
                motionTokenStr(m->token));
        }
    }
#else
    for (m=mlist; m; mp=m, m = m->c_next) {
        if (m->b_angle == 0xff) {
            printf("AA2 0x%08x: %04x Pause\n",
                m->b_atime,
                m->c_time);
        } else {
            printf("AA2 0x%08x: %04x shape=%d %s a=%02x %c %d%s %s d=%2d imp=%04x %04x MaxDMag=%02x maxStep=%02x "
                "ad=%3d flg=%02x %s  %02x  %s\n",
                m->b_atime,
                m->b_dtime,
                0, //m->c_shape,
                motionShapeStr[m->c_shape],
                m->c_best_angle,
                quadStr[a2quad(m->c_best_angle)][0],
                m->c_oct,
                octStr[m->c_oct],
                octStr2[m->c_oct],
                m->c_dir,
                m->c_imp,
                0, //m->c_isum,
                m->b_deltamag, //m->c_maxdmag,
                0, //m->c_maxstep,
                (m==mlist || m->b_angle == 0xff || mp->b_angle == 0xff) ? 
                        0:adiff(m->c_best_angle,mp->c_best_angle),
                m->c_flags,
                flagStr(m->c_flags),
                0, //m->token,
                motionTokenStr(m->token));
        }
    }
#endif
    printf("\n\n");
}

//===========================================================================
// initialPrint() - print initial motion data
//===========================================================================
static void initialPrint(Motion *mlist)
{
    Motion *m;
    if (OPT_silent) return;

#if 0
    for (m=mlist; m->b_angle != 0xfe; m++) {
        if (m->b_angle == 0xff) {
            printf("AA1 0x%08x: %04x Pause\n",
                m->b_atime,
                m->b_dtime);
        } else {
            printf("AA1 0x%08x: %04x a=%02x %c %c imp=%04x mag=%02x %02x %02x dmag=%02x %02x (%5.3f)"
                " flg=%02x %s\n",
                m->b_atime,
                m->b_dtime,
                m->b_angle,
                quadStr[a2quad(m->b_angle)][0],
                octStr2[a2oct(m->b_angle)][0],
                m->b_imp,
                m->b_minmag,
                0, //m->b_avgmag,
                m->b_maxmag,
                m->b_maxmag-m->b_minmag,
                m->b_deltamag,
                (double)m->b_deltamag/(double)m->b_maxmag,
                m->b_flags,
                flagStr(m->b_flags));
        }
    }
#else
    for (m=mlist; m->b_angle != 0xfe; m++) {
        if (m->b_angle == 0xff) {
            printf("AA1 0x%08x: %04x Pause\n",
                m->b_atime,
                m->b_dtime);
        } else {
            printf("AA1 0x%08x: %04x a=%02x %c %c imp=%04x mag=%02x %02x %02x dmag=%02x %02x (%5.3f)"
                " flg=%02x %s\n",
                m->b_atime,
                m->b_dtime,
                m->b_angle,
                quadStr[a2quad(m->b_angle)][0],
                octStr2[a2oct(m->b_angle)][0],
                m->b_imp,
                m->b_minmag,
                0, //m->b_avgmag,
                m->b_maxmag,
                m->b_maxmag-m->b_minmag,
                m->b_deltamag,
                (double)m->b_deltamag/(double)m->b_maxmag,
                m->b_flags,
                flagStr(m->b_flags));
        }
    }
#endif
    printf("\n\n");
}

//===========================================================================
// parse1()
//===========================================================================
void parse1(Motion *mlist, int mcnt)
{
    Motion *m;
    Motion *ms = 0;
    int maxstep = 0;

    for (m=mlist; m->b_angle != 0xfe; m++) {
        int o = a2oct(m->b_angle);
        int restart = 0;
        int ad = 0;
        int step = 0;
        int dir = 0;

        if (m->b_angle == 0xfe) {
            m = 0;
        }

        if (!m || !ms) {
            restart = 1;
        } else if (m->b_angle == 0xff) {
            restart = ms->b_angle != 0xff;
        } else if (ms->b_angle == 0xff) {
            restart = 1;
        } else {
            restart = o != a2oct(ms->b_angle);
            ad = adiff(m->b_angle, (m-1)->b_angle);
            step = ABS(ad);
            dir = ad < 0 ? -1 : 1;
        }

        if (step > maxstep) {
            maxstep = step;
        }

        if (restart) {
            if (ms) {
                ms->c_asum += step;
                if (dir != ms->c_dir) {
                    ms->c_flags &= ~MFLG_MONO;
                }
                if ((ms->c_flags & MFLG_MONO) == 0) {
                    ms->c_asum = 0;
                    ms->c_dir = 0;
                }

                if (ms->b_angle == 0xff) {
                    ms->c_shape = MSHP_PAUSE;
                } else if (ms == mlist || !m || (ms-1)->b_angle == 0xff || m->b_angle==0xff) {
                    ms->c_shape = MSHP_PULSE;
                } else if ((ms->c_flags&MFLG_MONO) && maxstep<=5 && ms->c_asum >= 4) {
                    ms->c_shape = MSHP_DIRTY_CIRCLE;
                    if (maxstep<=3 && ms->c_maxdmag<8 && (ms->c_flags & MFLG_ZERO)==0) {
                        ms->c_shape = MSHP_CLEAN_CIRCLE;
                    }
                } else {
                    ms->c_shape = MSHP_PULSE;
                }

                ms->c_next = m;
                m->c_prev = ms;
            }
            if (!m) break;
            ms = m;
            ms->c_dir = dir;
            ms->c_cnt = 1;
            ms->c_imp = ms->b_imp;
            ms->c_time = ms->b_dtime;
            ms->c_maxstep = maxstep;
            ms->c_oct = o;
            ms->c_best_angle = ms->b_angle;
            ms->c_best_mag = ms->b_maxmag;
            ms->c_flags = ms->b_flags | MFLG_MONO;
            ms->c_maxdmag = ms->b_deltamag;
            ms->c_asum = step;
            maxstep = step;
            ms->c_next = 0;
        } else {
            ms->c_cnt++;
            ms->c_time += m->b_dtime;
            if (m->b_angle == 0xff) {
                // double pause - ignore
            } else {
                if (dir != ms->c_dir) {
                    ms->c_flags &= ~MFLG_MONO;
                }
                ms->c_imp += m->b_imp;
                if (m->b_maxmag > ms->c_best_mag) {
                    ms->c_best_angle = m->b_angle;
                    ms->c_best_mag   = m->b_maxmag;
                }
                ms->c_flags |= m->b_flags;
                ms->c_maxdmag = MAX(ms->c_maxdmag, m->b_deltamag);
                ms->c_asum += step;
            }
        }
    }
}

#if 0
//===========================================================================
// findCircles()
//===========================================================================
void findCircles(Motion *mlist, int mcnt)
{
    Motion *ms, *m, *mp;
    int asum;
    int got_pulse = 0;

    for (ms=mlist; ms; mp2=ms, ms=ms->c_next) {
        ms->cc_shape = ms->c_shape;

        if (ms->c_shape == MSHP_PAUSE) continue;
        if (ms->c_shape == MSHP_PULSE) continue;

        asum = ms->c_asum;
        got_pulse = 0;

        for (mp=ms, m=ms->c_next; m; mp=m, m=m->c_next) {
            m->cc_shape = m->c_shape;
            if (got_pulse) break;
            if (m->c_shape == MSHP_PAUSE) break;
            if (mp->dir && m->dir && mp->dir!=m->dir) break;

            if (m->c_shape == MSHP_PULSE) got_pulse = 1;

            

            if (

        }
    }
}

//===========================================================================
// combinePulses()
//===========================================================================
void combinePulses(Motion *mlist, int mcnt)
{
    Motion *m, *ms, *mp, *mp2;
    int best_imp = 0;
    int ad;

    mp2 = 0;
    for (ms=mlist; ms; mp2=ms, ms = ms->cc_next) {
        ms->cc_imp = ms->c_imp;
        ms->cc_next = ms->c_next;
        ms->cc_best_angle = ms->c_best_angle;
        ms->cc_shape = ms->c_shape;
        if (ms->cc_shape == MSHP_DIRTY_CIRCLE && mp2 && mp2->cc_shape == MSHP_CLEAN_CIRCLE) continue;
        if (ms->cc_shape != MSHP_PULSE && ms->cc_shape != MSHP_DIRTY_CIRCLE) continue;
        best_imp = ms->c_imp;
        for (mp=ms, m=ms->c_next; m; mp=m, m=m->c_next) {
            if (m->c_shape != MSHP_PULSE && m->c_shape != MSHP_DIRTY_CIRCLE) break;
            if (m->c_shape == MSHP_DIRTY_CIRCLE) {
                Motion *m2;
                for (m2=m->c_next; m2; m2=m2->c_next) {
                    if (m2->c_shape != MSHP_DIRTY_CIRCLE) break;
                }
                if (m2 && m2->c_shape == MSHP_CLEAN_CIRCLE) break;
            }
            ad = adiff(m->c_best_angle,mp->c_best_angle);
            if (ABS(ad) > 5) break;
            if (m->c_imp > best_imp) {
                best_imp = m->c_imp;
                ms->cc_best_angle = m->c_best_angle;
            }
            ms->cc_imp += m->c_imp;
            ms->cc_next = m->c_next;
        }
    }
}
#endif

#if 0
//===========================================================================
// findStrongPulses()
//===========================================================================
void findStrongPulses(Motion *mlist, int mcnt)
{
    Motion *ms, *mp=0, *m_best_mag=0, *m_best_imp=0;

    for (ms=mlist; ms; mp2=ms, ms=ms->c_next) {
        int restart = 0;
        if (ms->c_shape == MSHP_PAUSE) {
            restart = 1;
            mp = 0;
        } else if (!mp) {
            restart = 1;
            mp = ms;
        } else {
            int doct = a2oct(ms->c_best_angle) - a2oct(mp->c_best_angle);
            doct &= 7;
            if (doct & 4) doct -= 8;
            if (ABS(doct) > 1) {
                restart = 1;
            } else {
                if (ms->c_imp > 
            }
            mp = ms;
        }


        if (restart) {
            if (m_best_mag) {
                m_best_mag->c_flags |= MFLG_BEST_MAG;
            }
            if (m_best_imp) {
                m_best_imp->c_flags |= MFLG_BEST_IMP;
            }
            m_best_mag = mp;
            m_best_imp = mp;
        }


        ms->cc_imp = ms->c_imp;
        ms->cc_next = ms->c_next;
        ms->cc_best_angle = ms->c_best_angle;
        ms->c_shape = ms->c_shape;
        if (ms->c_shape == MSHP_DIRTY_CIRCLE && mp2 && mp2->c_shape == MSHP_CLEAN_CIRCLE) continue;
        if (ms->c_shape != MSHP_PULSE && ms->c_shape != MSHP_DIRTY_CIRCLE) continue;
        best_imp = ms->c_imp;
        for (mp=ms, m=ms->c_next; m; mp=m, m=m->c_next) {
            if (m->c_shape != MSHP_PULSE && m->c_shape != MSHP_DIRTY_CIRCLE) break;
            if (m->c_shape == MSHP_DIRTY_CIRCLE) {
                Motion *m2;
                for (m2=m->c_next; m2; m2=m2->c_next) {
                    if (m2->c_shape != MSHP_DIRTY_CIRCLE) break;
                }
                if (m2 && m2->c_shape == MSHP_CLEAN_CIRCLE) break;
            }
            ad = adiff(m->c_best_angle,mp->c_best_angle);
            if (ABS(ad) > 5) break;
            if (m->c_imp > best_imp) {
                best_imp = m->c_imp;
                ms->cc_best_angle = m->c_best_angle;
            }
            ms->cc_imp += m->c_imp;
            ms->cc_next = m->c_next;
        }
    }
}
#endif

//===========================================================================
// insertNode()
//===========================================================================
Motion *insertNode(Motion *mlist, Motion *prev)
{
    // NOTE: MEMORY LEAK! these are never freed
    Motion *m = malloc(sizeof(Motion));
    memcpy(m,prev,sizeof(*m));
    m->c_prev = prev;
    m->c_next = prev->c_next;
    prev->c_next = m;
    if (prev->c_next) prev->c_next->c_prev = m;

    m->b_atime = m->c_next->b_atime;
    m->b_dtime = 0;
    m->b_imp   = 0;
    m->b_minmag = 0;
    m->b_avgmag = 0;
    m->b_maxmag = 0;
    m->b_deltamag = 0;
    //m->b_angle;
    //m->b_flags;

    //m->c_shape;
    m->c_time = 0;
    m->c_cnt = 0;
    m->c_imp = 0;
    m->c_isum = 0;
    //m->c_dir;
    m->c_maxstep = 0;
    //m->c_best_angle;
    //m->c_best_mag;
    //m->c_flags;
    //m->c_maxdmag;
    //m->c_asum;

    //m->token;
    return m;
}

//===========================================================================
// fillCircleGaps()
//===========================================================================
int fillCircleGaps(Motion *mlist, int mcnt)
{
    Motion *ms, *m, *mg;
    int cnt0, cnt1;
    int oct0, oct;
    int added = 0;

    printf("start fillCircleGaps\n");

    //
    // check counter-clockwise circles (dir == 1)
    //
    for (ms=mlist; ms; ) {
        if (ms->c_dir < 0) {
            ms = ms->c_next;
            continue;
        }
        cnt0 = 1;
        oct0 = ms->c_oct;
        for (m=ms->c_next; m; m=m->c_next) {
            if (m->c_dir < 0) {
                break;
            }
            oct = m->c_oct;
            if (((oct - oct0)&7) != 1) break;
            cnt0++;
            oct0 = oct;
        }
    
        if (!m) break;
        if (m->c_dir < 0 || ((oct - oct0)&7) != 2) {
            ms = m;
            continue;
        }
        
        mg = m;
        cnt1 = 1;
        oct0 = oct;
        for (m=m->c_next; m; m=m->c_next) {
            oct = m->c_oct;
            if (((oct - oct0)&7) != 1) break;
            cnt1++;
            oct0 = oct;
        }

        if (cnt0>=2 && cnt1>=2 && (cnt0+cnt1 > 5)) {
            m = insertNode(mlist,mg->c_prev);
            m->c_dir = 1;
            m->c_maxstep = 0;
            m->c_oct = (mg->c_oct - 1) & 7;
            m->c_best_angle = oct2a(m->c_oct);
            m->b_angle = m->c_best_angle;
            m->c_flags = MFLG_MONO | MFLG_MISSING_CIRCLE;
            m->c_shape = MSHP_CIRCLE_GAP;
            added++;
        }
        ms = ms->c_next;
    }

    //
    // check counter-clockwise circles (dir == -1)
    //
    for (ms=mlist; ms; ) {
        if (ms->c_dir > 0) {
            ms = ms->c_next;
            continue;
        }
        cnt0 = 1;
        oct0 = ms->c_oct;
        for (m=ms->c_next; m; m=m->c_next) {
            if (m->c_dir > 0) {
                break;
            }
            oct = m->c_oct;
            if (((oct - oct0)&7) != 7) break;
            cnt0++;
            oct0 = oct;
        }
    
        if (!m) break;
        if (m->c_dir > 0 || ((oct - oct0)&7) != 6) {
            ms = m;
            continue;
        }
        
        mg = m;
        cnt1 = 1;
        oct0 = oct;
        for (m=m->c_next; m; m=m->c_next) {
            oct = m->c_oct;
            if (((oct - oct0)&7) != 7) break;
            cnt1++;
            oct0 = oct;
        }

        if (cnt0>=2 && cnt1>=2 && (cnt0+cnt1 > 5)) {
            m = insertNode(mlist,mg->c_prev);
            m->c_dir = -1;
            m->c_maxstep = 0;
            m->c_oct = (mg->c_oct + 1) & 7;
            m->c_best_angle = oct2a(m->c_oct);
            m->b_angle = m->c_best_angle;
            m->c_flags = MFLG_MONO | MFLG_MISSING_CIRCLE;
            m->c_shape = MSHP_CIRCLE_GAP;
            added++;
        }
        ms = ms->c_next;
    }
    printf("end   fillCircleGaps (added %d)\n",added);
    return added;
}

//===========================================================================
// findWeakCircles()
//===========================================================================
void findWeakCircles(Motion *mlist, int mcnt)
{
    Motion *ms, *m, *m2;
    int dcnt, ccnt;

    printf("start findWeakCircles\n");
    for (ms=mlist; ms; ) {

        ccnt = dcnt = 0;
        for (m=ms; m; m=m->c_next) {
            if (m->c_shape == MSHP_DIRTY_CIRCLE) {
                dcnt++;
            } else if (m->c_shape == MSHP_CLEAN_CIRCLE) {
                ccnt++;
            } else {
                break;
            }
        }
        if (ccnt > 4 || (ccnt>0 && ccnt+dcnt>5)) {
            for (m2=ms; m2!=m; m2=m2->c_next) {
                m2->c_flags |= MFLG_CIRCLE;
            }
            m2 = ms->c_prev;
            if (m2->c_shape == MSHP_PULSE && (m2->c_flags & MFLG_MONO) && m2->c_dir == ms->c_dir) {
                int d = odiff(m2->c_oct, ms->c_oct);
                if (ABS(d) < 2) {
                    m2->c_flags |= MFLG_CIRCLE;
                }
            }
            m2 = m->c_prev;
            if (m->c_shape == MSHP_PULSE && (m->c_flags & MFLG_MONO) && m->c_dir == ms->c_dir) {
                int d = odiff(m->c_oct, m2->c_oct);
                if (ABS(d) < 2) {
                    m->c_flags |= MFLG_CIRCLE;
                }
            }
        } else if (ccnt>0) {
            for (m2=ms; m2!=m; m2=m2->c_next) {
                m2->c_shape = MSHP_DIRTY_CIRCLE;
            }
        }
        ms = m==ms ? m->c_next : m;
    }
    printf("end   findWeakCircles\n");
}

//===========================================================================
// findBestPulses()
//===========================================================================
void findBestPulses(Motion *mlist, int mcnt)
{
    Motion *m, *ms;
    Motion *best_imp=0, *best_mag=0;
    int d;
    int isum = 0;

    printf("start findBestPulses\n");
    for (ms=mlist; ms; ms = ms->c_next) {
        ms->c_isum = ms->c_imp;
        if (ms->c_shape == MSHP_DIRTY_CIRCLE && ms->c_prev->c_shape == MSHP_CLEAN_CIRCLE) continue;
        if (ms->c_shape != MSHP_PULSE && ms->c_shape != MSHP_DIRTY_CIRCLE) continue;
        best_imp = ms;
        best_mag = ms;
        isum = ms->c_imp;
        for (m=ms->c_next; m; m=m->c_next) {
            m->c_isum = m->c_imp;
            if (m->c_shape != MSHP_PULSE && m->c_shape != MSHP_DIRTY_CIRCLE) break;
            if (m->c_shape == MSHP_DIRTY_CIRCLE && (m->c_flags & MFLG_CIRCLE)) break;

            d = adiff(m->c_best_angle,m->c_prev->c_best_angle);
            if (ABS(d) > 5) break;

            d = odiff(m->c_oct, m->c_prev->c_oct);
            if (ABS(d) > 1) break;

            if (m->c_best_mag > best_mag->c_best_mag) {
                best_mag = m;
            }
            if (m->c_imp > best_imp->c_imp) {
                best_imp = m;
            }
            isum += m->c_imp;
        }
        best_imp->c_isum = isum;
        if (isum > 0x500) {
            best_imp->c_flags |= MFLG_BEST_IMP;
            best_mag->c_flags |= MFLG_BEST_MAG;
            if (best_imp == best_mag) {
                switch(best_imp->c_shape) {
                    case MSHP_PULSE:
                        best_imp->c_shape = MSHP_PULSE_PLUS;
                        break;
                    case MSHP_DIRTY_CIRCLE:
                        best_imp->c_shape = MSHP_DIRTY_CIRCLE_PLUS;
                        break;
                    case MSHP_CLEAN_CIRCLE:
                        best_imp->c_shape = MSHP_CLEAN_CIRCLE_PLUS;
                        break;
                }
            }
        }
        ms = m->c_prev;
    }
    printf("end   findBestPulses\n");
}


//===========================================================================
// markLast() - mark last few(3) pulses as unimportant
//===========================================================================
void markLast(Motion *mlist, int mcnt)
{
    Motion *m;
    int cnt = 0;

    if (!mlist) return;
    for (m=mlist; m->c_next; m=m->c_next);
    while(m && cnt<3) {
        switch(m->c_shape) {
            case MSHP_PAUSE:
                break;
            case MSHP_CLEAN_CIRCLE:
            case MSHP_DIRTY_CIRCLE:
            case MSHP_PULSE:
            case MSHP_CIRCLE_GAP:
                cnt++;
                break;

            case MSHP_CLEAN_CIRCLE_PLUS:
                m->c_shape = MSHP_CLEAN_CIRCLE;
                cnt++;
                break;
            case MSHP_DIRTY_CIRCLE_PLUS:
                m->c_shape = MSHP_DIRTY_CIRCLE;
                cnt++;
                break;
            case MSHP_PULSE_PLUS:
                m->c_shape = MSHP_PULSE;
                cnt++;
                break;
        }
        m = m->c_prev;
    }
}

#if 0
//===========================================================================
// removeWeakPulses()
//===========================================================================
void removeWeakPulses(Motion *mlist, int mcnt)
{
    Motion *m, *ms, *mp, *mp2;
    int d;
    int isum = 0;

    printf("start removeWeakPulses\n");
    mp = 0;
    for (ms=mlist; ms; mp=ms, ms = ms->c_next) {
        if (





        ms->c_isum = ms->c_imp;
        if (ms->c_shape == MSHP_DIRTY_CIRCLE && mp2 && mp2->c_shape == MSHP_CLEAN_CIRCLE) continue;
        if (ms->c_shape != MSHP_PULSE && ms->c_shape != MSHP_DIRTY_CIRCLE) continue;
        best_imp = ms;
        best_mag = ms;
        isum = ms->c_imp;
        for (mp=ms, m=ms->c_next; m; mp=m, m=m->c_next) {
            m->c_isum = m->c_imp;
            if (m->c_shape != MSHP_PULSE && m->c_shape != MSHP_DIRTY_CIRCLE) break;
            if (m->c_shape == MSHP_DIRTY_CIRCLE && (m->c_flags & MFLG_CIRCLE)) break;

            d = adiff(m->c_best_angle,mp->c_best_angle);
            if (ABS(d) > 5) break;

            d = adiff(a2oct(m->c_best_angle)<<2, a2oct(mp->c_best_angle)<<2);
            if (ABS(d) > 4) break;

            if (m->c_best_mag > best_mag->c_best_mag) {
                best_mag = m;
            }
            if (m->c_imp > best_imp->c_imp) {
                best_imp = m;
            }
            isum += m->c_imp;
        }
        best_imp->c_isum = isum;
        best_imp->c_flags |= MFLG_BEST_IMP;
        best_mag->c_flags |= MFLG_BEST_MAG;
        ms = mp;
    }
    printf("end   removeWeakPulses\n");
}
#endif

//===========================================================================
// tokenize()
//===========================================================================
void tokenize(Motion *mlist, int mcnt)
{
    Motion *m;
    for (m=mlist; m; m=m->c_next) {
        int a = m->c_best_angle;
        if (m->c_shape != MSHP_PULSE_PLUS) {
            while(1) {
                int d = odiff(m->c_oct, a2oct(a));
                if (!d) break;
                a = (a + (d<0?-1:1)) & 0x1f;
            }
        }
        m->token = TOKEN_CREATE(m->c_shape, m->c_dir, a);
    }
}



//===========================================================================
// look()
//===========================================================================
void look(Motion *mlist, int mcnt)
{
#if 1
    printf("\n\n");

    parse1(mlist,mcnt);

    if (!OPT_silent) {
        printf("\nPOST parse1\n\n");
        lookPrint(mlist,mcnt);
    }

    while(fillCircleGaps(mlist,mcnt));

    if (!OPT_silent) {
        printf("\nPOST fillCircleGaps\n\n");
        lookPrint(mlist,mcnt);
    }

    findWeakCircles(mlist,mcnt);

    if (!OPT_silent) {
        printf("\nPOST findWeakCircles\n\n");
        lookPrint(mlist,mcnt);
    }

    findBestPulses(mlist,mcnt);

    if (!OPT_silent) {
        printf("\nPOST findBestPulses\n\n");
        lookPrint(mlist,mcnt);
    }

    markLast(mlist,mcnt);

    if (!OPT_silent) {
        printf("\nPOST markLast\n\n");
        lookPrint(mlist,mcnt);
    }

    tokenize(mlist,mcnt);

    if (!OPT_silent) {
        printf("\nPOST tokenize\n\n");
        lookPrint(mlist,mcnt);
    }
#else
    printf("\n\n");
    parse1(mlist,mcnt);

    lookPrint(mlist,mcnt);
    while(fillCircleGaps(mlist,mcnt));
    lookPrint(mlist,mcnt);
    findWeakCircles(mlist,mcnt);
    lookPrint(mlist,mcnt);
    findBestPulses(mlist,mcnt);

    markLast(mlist,mcnt);
    tokenize(mlist,mcnt);

    lookPrint(mlist,mcnt);
#endif
}

//===========================================================================
// getline2()
//===========================================================================
int getline2(FILE *fp, char *line, char *end)
{
    int c = getc(fp);
    if (c==EOF) return 0;

    end--;
    while(c!='\n' && c!=EOF && line != end) {
        *(line++) = c;
        c = getc(fp);
    }
    *line = 0;
    return 1;
}

//===========================================================================
// getline()
//===========================================================================
int getline(FILE *fp, Motion *m)
{
    float list[6];
    int idx = 0;
    char line[1000];
    char *s, *e;

    while(1) {
        if (!getline2(fp,line,line+sizeof(line))) {
            return 0;
        }
        s = line;

        while(1) {
            while(isspace(*s)) s++;

            if (*s == '#' || *s == 0) {
                break;
            }

            list[idx] = strtod(s,&e);
            if (!e || e==s) {
                fprintf(stderr,"Error parsing line:\n    %s\n",line);
                break;
            }
            s = e;
            idx++;
            if (idx >= sizeof(list)/sizeof(list[0])) break;
        }

        if (idx) {
            while (idx < sizeof(list)/sizeof(list[0])) {
                list[idx++] = 0.0;
            }
            m->b_dtime  = list[0];
            m->b_angle  = list[1];
            m->b_imp    = list[2];
            m->b_minmag = list[3];
            m->b_maxmag = list[4];
            m->b_flags  = list[5];
            m->b_avgmag = (m->b_minmag + m->b_maxmag)/2;
            m->b_deltamag = m->b_maxmag - m->b_minmag;
            if (m->b_flags & MFLG_ZERO) {
                m->b_deltamag = m->b_maxmag;
            }

            if (m->b_dtime > 0) {
                m->b_avgmag = m->b_imp / m->b_dtime;
            }

            if (m->b_angle == 0xff) {
                m->b_imp    = 0;
                m->b_minmag = 0;
                m->b_maxmag = 0;
                m->b_flags  = 0;
                m->b_avgmag = 0;
                m->b_deltamag = 0;
            }
            return 1;
        }
    }
}

//===========================================================================
// get() - read the file
//===========================================================================
int get(FILE *in, Motion *m, Motion *end)
{
    int atime = 0;
    int cnt = 0;

    //
    // start with a pause
    //
    m->b_atime  = 0;
    m->b_angle  = 0xff;
    m++;
    cnt++;

    while(m != end-1 && getline(in,m)) {
        m->b_atime = atime;
        atime += m->b_dtime;
        m++;
        cnt++;
    }

    //
    // end with a pause
    //
    m->b_atime  = 0;
    m->b_angle  = 0xff;
    m++;
    cnt++;

    m->b_angle = 0xfe;
    return cnt;
}


//===========================================================================
// compareToken()
//===========================================================================
void compareToken(int *ignoreMotion, int *ignoreSpell, int *val, int stok, int mtok)
{
    int mshape = TOKEN_SHAPE(mtok);
    int mdir   = TOKEN_DIR(mtok);
    int mangle = TOKEN_ANGLE(mtok);
    int sshape = TOKEN_SHAPE(stok);
    int sdir   = TOKEN_DIR(stok);
    int sangle = TOKEN_ANGLE(stok);
    int d;
    int add = 0;

    *ignoreMotion = ignoreMotions[mshape];
    *ignoreSpell  = ignoreSpells[sshape];
    *val        = 10000;
    
    switch(sshape) {
        case SSHP_PAUSE:
            *val = scorePause[mshape];
        case SSHP_CIRCLE:
        case SSHP_OCIRCLE:
            switch(mshape) {
                case MSHP_CLEAN_CIRCLE_PLUS:
                case MSHP_DIRTY_CIRCLE_PLUS:
                case MSHP_CLEAN_CIRCLE:
                case MSHP_DIRTY_CIRCLE:
                case MSHP_CIRCLE_GAP:
                    if (mdir != sdir) break;
                    d = a2oct(mangle) - a2oct(sangle);
                    if (d) break;
                    *val = 0;
                    break;


                case MSHP_PULSE:
                case MSHP_PULSE_PLUS:
                    d = a2oct(mangle) - a2oct(sangle);
                    if (d) break;
                    *val = 0;
                    break;

                case MSHP_PAUSE:
                    break;
            }
            break;
        case SSHP_PULSE:
        case SSHP_OPULSE:
            switch(mshape) {
                case MSHP_CLEAN_CIRCLE:
                    //add += 4;
                case MSHP_DIRTY_CIRCLE:
                    //add += 2;
                case MSHP_PULSE:
                    //add += 2;
                case MSHP_CLEAN_CIRCLE_PLUS:
                case MSHP_DIRTY_CIRCLE_PLUS:
                case MSHP_PULSE_PLUS:
                    d = adiff(mangle, sangle);
                    d = ABS(d);
                    if (d < 10) {
                        *val = scorePulse[d] + add;
                    }
                    break;

                case MSHP_PAUSE:
                case MSHP_CIRCLE_GAP:
                    break;
            }
            break;
        case SSHP_WILD:
            *val = 1;
            break;
        default:
            fatalError("Bad token in compareToken\n");
    }
}

//===========================================================================
// printColor()
//===========================================================================
void printColor(int diff)
{
    if (diff<0) diff = -diff;
    printf("%c[%sm",0x1b,
        (diff)>=10 ? "37;41" :
        (diff)>=4  ? "30;43" :
        (diff)>=1  ? "37;44" : "37;42");
}

//===========================================================================
// scoreSpell()
//===========================================================================
int scoreSpell(Spell *spell, Motion *mlist, char *path, int doprint)
{
    Motion *m;
    unsigned char *spellTokens;
    unsigned char *s;
    static int mtx[1000];
    int scnt,mcnt,slen;
    int ignoreMotion, ignoreSpell, match, val, corner, lastval = 0;

    //
    // get rid of leading pause
    //
    for (s=spell->tokens; *s && TOKEN_SHAPE(*s) == SSHP_PAUSE; s++);
    spellTokens = s;

    if (doprint==2) {
        printf("               ");
        for (scnt=1, s=spellTokens; *s; scnt++, s++) {
            printf("%5s ",spellTokenStr(*s));
        }
        printf("\n               ");
        for (scnt=1, s=spellTokens; *s; scnt++, s++) {
            printf(" 0x%02x ",*s);
        }
        printf("\n          %-4d ",0);
    } else if (doprint==3) {
        printf("  %5s\n",spell->name);
        printf("  %5s     %5s\n","spell","motion");
    }

    mtx[0] = 0;
    for (scnt=1, s=spellTokens; *s; scnt++, s++) {
        compareToken(&ignoreMotion, &ignoreSpell, &match, *s, MSHP_PAUSE<<4);
        mtx[scnt] = val = mtx[scnt-1] + ignoreSpell;
        if (doprint==2) {
            if (path[scnt]) {
                printColor(val-lastval);
                lastval = val;
            }
            printf("<%-4d",mtx[scnt]);
            printf("%c[0m ",0x1b);
        } else if (doprint==3 && path[scnt]) {
            printf("  %5s %d   %5s   ",
                spellTokenStr(*s),
                a2oct(a2oct(TOKEN_ANGLE(*s))),
                "skip");
            printColor(ignoreSpell);
            printf("%4d  %4d%c[0m\n",ignoreSpell,val,0x1b);
        } else if (doprint == -1) {
            path[scnt] = '<';
        }
    }
    slen = scnt;

    for (mcnt=1, m = mlist; m; mcnt++, m=m->c_next) {
        corner = mtx[0];
        compareToken(&ignoreMotion, &ignoreSpell, &match, PAUSE, m->token);
        mtx[0] = val = corner + ignoreMotion;
        if (doprint==2) {
            printf("\n%5s %02x ",motionTokenStr(m->token),0/*m->token*/);
            if (path[slen*mcnt]) {
                printColor(match-lastval);
                lastval = val;
            }
            printf("^%-4d",mtx[0]);
            printf("%c[0m ",0x1b);
        } else if (doprint==3 && path[slen*mcnt]) {
                    printf("  %5s   %d %5s   ",
                        "skip",
                        a2oct(a2oct(TOKEN_ANGLE(m->token))),
                        motionTokenStr(m->token));
            printColor(ignoreMotion);
            printf("%4d  %4d%c[0m\n",ignoreMotion,val,0x1b);
        } else if (doprint == -1) {
            path[slen*mcnt] = '^';
        }
        for (scnt=1, s=spellTokens; *s; scnt++, s++) {
            char *src = "\\";
            compareToken(&ignoreMotion, &ignoreSpell, &match, *s, m->token);

            val = match + corner;
            if (val > ignoreMotion + mtx[scnt]) {
                val = ignoreMotion + mtx[scnt];
                src="^";
            }
            if (val > ignoreSpell + mtx[scnt-1]) {
                val = ignoreSpell + mtx[scnt-1];
                src="<";
            }
            if (doprint==2) {
                if (path[slen*mcnt+scnt]) {
                    printColor(val-lastval);
                    lastval = val;
                }
                printf("%s%-4d",
                    src,val);
                printf("%c[0m ",0x1b);
            } else if (doprint==3 && path[slen*mcnt+scnt]) {
                int delta = 1000;
                switch(src[0]) {
                    case '^':
                        printf("  %5s   %d %5s   ",
                            "skip",
                            a2oct(a2oct(TOKEN_ANGLE(m->token))),
                            motionTokenStr(m->token));
                        delta = ignoreMotion;
                        break;
                    case '<':
                        printf("  %5s %d   %5s   ",
                            spellTokenStr(*s),
                            a2oct(a2oct(TOKEN_ANGLE(*s))),
                            "skip");
                        delta = ignoreSpell;
                        break;
                    case '\\':
                        printf("  %5s %d %d %5s   ",
                            spellTokenStr(*s),
                            a2oct(a2oct(TOKEN_ANGLE(*s))),
                            a2oct(a2oct(TOKEN_ANGLE(m->token))),
                            motionTokenStr(m->token));
                        delta = match;
                        break;
                    default:
                        printf("  %5s     %5s   ",
                            "????",
                            "????");
                        break;
                }
                printColor(delta);
                printf("%4d  %4d%c[0m\n",delta,val,0x1b);
            } else if (doprint==-1) {
                path[slen*mcnt+scnt] = src[0];
            }

            corner = mtx[scnt];
            mtx[scnt] = val;
        }
    }
    if (doprint==2) {
        printf("\n");
    } else if (doprint == -1) {
        int i;
        int mlen = mcnt;

        //
        // find path
        //
        path[0] = '*';

        scnt--;
        mcnt--;
        while(scnt>=0 && mcnt>=0) {
            int c = path[slen*mcnt+scnt];
            path[slen*mcnt+scnt] = '*';
            switch(c) {
                case '<':
                    scnt--;
                    break;
                case '^':
                    mcnt--;
                    break;
                case '\\':
                case '*':
                    mcnt--;
                    scnt--;
                    break;
            }
        }
        for (i=0; i<mlen*slen; i++) {
            if (path[i] != '*') path[i] = 0;
        }
    }

    if (doprint>0 && doprint<3) {
        printf("Score vs %-25s = %8d\n",spell->name,val);
    }
    spell->score = val;
    return val;
}

typedef struct SpellCreateInfoRec {
    int     lastAngle;
    int     prevCircleCnt;
} SpellCreateInfo;

//===========================================================================
// spellInit()
//===========================================================================
void spellInit(SpellCreateInfo *si, int startAngle)
{
    memset(si,0,sizeof(*si));
    si->lastAngle = startAngle;
}

//===========================================================================
// spellCircleSub()
//===========================================================================
unsigned char *spellCircleSub(unsigned char *t, int dir, int *oct, int optional)
{
    static int table_cw[8]  = { 6,7,0,1,2,3,4,5 };
    static int table_ccw[8] = { 2,3,4,5,6,7,0,1 };
    int *table = dir<0 ? table_cw : table_ccw;

    if (optional) {
        *(t++) = TOKEN_CREATE(SSHP_OCIRCLE, dir, oct2a(table[*oct]));
    } else {
        *(t++) = TOKEN_CREATE(SSHP_CIRCLE,  dir, oct2a(table[*oct]));
    }
    *oct = (*oct + dir) & 7;
    return t;
}

//===========================================================================
// spellCircle()
//===========================================================================
unsigned char *spellCircle(unsigned char *t, SpellCreateInfo *si, int cnt)
{
    int oct = a2oct(si->lastAngle);
    int dir = 1;
    int prevdir;
    int cnt0 = 0, cnt1 = 0, cnt2 = 0;
    if (cnt < 0) {
        dir = -1;
        cnt = -cnt;
    }

    //
    // Circles always end with 
    //   - lastAngle pointing the way it is supposed to and 
    //   - 2nd to last optional circle pulse perpendicular to end direction
    //
    //   For example, if end angle is oct==2 (right)
    //      tokens = ... CW4 CW3 CW2 oCW1 oCW0 oCW7
    //

    si->lastAngle = oct2a(oct);

    switch(TOKEN_SHAPE(t[-1])) {
        case SSHP_PAUSE:
            *(t++) = TOKEN_CREATE(SSHP_OPULSE, 0, oct2a(oct));
            oct = (oct - dir) & 7;
            cnt0 = 1;
            cnt1 = 2+8-1-2 + (8*(cnt-1));
            cnt2 = 3;
            // NOTE: same lastAngle
            break;
            
        case SSHP_CIRCLE:
        case SSHP_OCIRCLE:
            prevdir = TOKEN_DIR(t[-1]);
            if (prevdir == dir) {
                t -= 3;
                oct = (oct - prevdir) & 7;
                cnt0 = 0;
                cnt1 = 2 + 8 - 2 + (8*(cnt-1));
                cnt2 = 3;
                // NOTE: same lastAngle
            } else {
                oct = (oct + 4) & 7;
                *(t++) = TOKEN_CREATE(SSHP_OPULSE, 0, oct2a(oct));
                *(t++) = TOKEN_CREATE(SSHP_OPULSE, 0, oct2a((oct+prevdir)&7));
                *(t++) = TOKEN_CREATE(SSHP_OPULSE, 0, oct2a(oct));
                oct = (oct - dir) & 7;
                cnt0 = 2;
                cnt1 = 2+8-2-2 + (8*(cnt-1));
                cnt2 = 3;   // circlSlop=5
                si->lastAngle = oct2a(oct);
            }
            break;

        case SSHP_OPULSE:
        case SSHP_PULSE:
            oct = (oct - dir) & 7;
            cnt0 = 3;
            cnt1 = 2+8-3-2 + (8*(cnt-1));
            cnt2 = 3;
            // NOTE: same lastAngle
            break;

        default:
            fatalError("Bad token in spellCircle() \n");
    }

    while(cnt0--) {
        t = spellCircleSub(t, dir, &oct, 1);
    }
    while(cnt1--) {
        t = spellCircleSub(t, dir, &oct, 0);
    }
    while(cnt2--) {
        t = spellCircleSub(t, dir, &oct, 1);
    }
    si->prevCircleCnt = cnt;
    return t;
}

//===========================================================================
// spellPulse()
//===========================================================================
unsigned char *spellPulse(unsigned char *t, SpellCreateInfo *si, int oct)
{
    int revoct = (oct + 4) & 7;
    int optional = 0;
    int dir, octc, octprev = a2oct(si->lastAngle);
    int cnt;

    switch(TOKEN_SHAPE(t[-1])) {
        case SSHP_PAUSE:
            *(t++) = TOKEN_CREATE(SSHP_OPULSE,0,oct2a(oct));
            break;

        case SSHP_OPULSE:
        case SSHP_PULSE:
            //
            // same direction as last pulse?
            //
            if (revoct == octprev) {
                optional = 1;
                *(t++) = TOKEN_CREATE(SSHP_OPULSE,0,oct2a(oct));

            //
            // opposite direction of previous
            //
            } else if (oct == octprev) {
                break;

            //
            // some other angle (less)
            //
            } else if (odiff(oct,octprev) > 0) {
                while(octprev != oct) {
                    t = spellCircleSub(t, 1, &octprev, 1);
                }
            } else {
                while(octprev != oct) {
                    t = spellCircleSub(t, -1, &octprev, 1);
                }
            }
            break;
            
        case SSHP_CIRCLE:
        case SSHP_OCIRCLE:
            dir = TOKEN_DIR(t[-1]);
            octc = a2oct(si->lastAngle);
//printf("octc=%d\n",octc);
            // t now points to the CW2  in example above
            t -= 4;

//printf("t[]=%02x\n",*t);
            // oct now points up (4) in example above
            octc = (octc - dir * 2) & 7;
            cnt = -1;

//printf("octc=%d cnt=%d\n",octc,cnt);

            if (si->prevCircleCnt > 1) {
                t -= 1;
                octc = (octc - dir) & 7;
                cnt -= 1;
            }
//printf("t[]=%02x\n",*t);
//printf("octc=%d cnt=%d\n",octc,cnt);

//printf("S octc(%d) +dir(%d) !=? oct=%d\n",octc,dir,oct);
            while(((octc+dir)&7) != oct) {
//printf("+ octc(%d) +dir(%d) !=? oct=%d\n",octc,dir,oct);
                t = spellCircleSub(t, dir, &octc, 0);
                cnt++;
            }
//printf("E octc(%d) +dir(%d) !=? oct=%d\n",octc,dir,oct);

            //
            // if less than full circle add an extra optional circle
            //
            if (cnt < 0) {
//printf("EXTRA\n");
                for (cnt=0; cnt<8; cnt++) {
                    t = spellCircleSub(t, dir, &octc, 1);
                }
            }

//printf("2 octc=%d\n",octc);
            t = spellCircleSub(t, dir, &octc, 1);
//printf("1 octc=%d\n",octc);
            t = spellCircleSub(t, dir, &octc, 1);
//printf("0 octc=%d revoct=%d\n",octc,revoct);
            break;

        default:
            fatalError("Bad token in spellCircle() \n");
    }

    if (optional) {
        *(t++) = TOKEN_CREATE(SSHP_OPULSE,0,oct2a(revoct));
    } else {
        *(t++) = TOKEN_CREATE(SSHP_PULSE,0,oct2a(revoct));
    }
#if 0
    *(t++) = TOKEN_CREATE(SSHP_OPULSE,0,oct2a(oct));
#endif
    si->lastAngle = oct2a(revoct);
    si->prevCircleCnt = 0;
    return t;
}

//===========================================================================
// spellPause()
//===========================================================================
unsigned char *spellPause(unsigned char *t, SpellCreateInfo *si)
{
    *(t++) = TOKEN_CREATE( SSHP_PAUSE, 0, 0);
    si->prevCircleCnt = 0;
    return t;
}

//===========================================================================
// createSpell()
//===========================================================================
int createSpell(Spell *s, int startAngle)
{
    SpellCreateInfo si[1];
    unsigned char *t = s->tokens;
    const char *p;
    int oct;

    spellInit(si,startAngle);

#define START_ANGLE_FROM_SPELL 1
#if START_ANGLE_FROM_SPELL
    startAngle = -1;
    for (p=s->phonemes; *p; p++) {
        switch(*p) {
            case 'e':
            case 'i':
            case 'a':
            case 'o':
            case 'u':
            case 'y':
            case ' ':
                break;
            case 'd': startAngle = oct2a(4); break;
            case 'k': startAngle = oct2a(2); break;
            case 'g': startAngle = oct2a(0); break;
            case 'f': startAngle = oct2a(6); break;
            case 'h': startAngle = oct2a(3); break;
            case 's': startAngle = oct2a(1); break;
            case 'm': startAngle = oct2a(7); break;
            case 'l': startAngle = oct2a(5); break;
            default:
                fatalError("ERROR: BAD PHONEME CHARACTER: %c\n    in '%s'\n",
                    *p,s->phonemes);
        }
        if (startAngle >= 0) {
            si->lastAngle = startAngle;
            break;
        }
    }
#endif

    *(t++) = TOKEN_CREATE( SSHP_PAUSE,0,0);

    for (p=s->phonemes; *p; p++) {
        switch(*p) {
            case 'e': t = spellCircle(t,si,+2); break;
            case 'i': t = spellCircle(t,si,+3); break;
            case 'a': t = spellCircle(t,si,-1); break;
            case 'o': t = spellCircle(t,si,-2); break;
            case 'u': t = spellCircle(t,si,-3); break;
            case 'y': t = spellCircle(t,si,+1); break;
            case 'd': t = spellPulse(t,si,4); break;
            case 'k': t = spellPulse(t,si,2); break;
            case 'g': t = spellPulse(t,si,0); break;
            case 'f': t = spellPulse(t,si,6); break;
            case 'h': t = spellPulse(t,si,3); break;
            case 's': t = spellPulse(t,si,1); break;
            case 'm': t = spellPulse(t,si,7); break;
            case 'l': t = spellPulse(t,si,5); break;
            case ' ': t = spellPause(t,si); break;
            default:
                fatalError("ERROR: BAD PHONEME CHARACTER: %c\n    in '%s'\n",
                    *p,s->phonemes);
        }
        if (t-s->tokens > sizeof(s->tokens)-10) {
            fprintf(stderr,"\n\n\n");
            fprintf(stderr,"ERROR: SPELL TOO LONG:\n");
            fprintf(stderr,"     in '%s'\n",
                s->phonemes);
            fprintf(stderr,"\n\n\n");
            exit(1);
        }
    }

    oct = a2oct(si->lastAngle);
    oct = (oct+4)&7;
    *(t++) = TOKEN_CREATE(SSHP_OPULSE,0,oct2a(oct));

    //*(t++) = TOKEN_CREATE(SSHP_WILD, 0, 0);
    //*(t++) = TOKEN_CREATE(SSHP_WILD, 0, 0);
    *t = 0;

    return t - s->tokens;
}

//===========================================================================
// spellLen() - find # of tokens in spell
//===========================================================================
int spellLen(Spell *s)
{
    unsigned char *t = s->tokens;
    while(*t) t++;
    return t-s->tokens;
}

//===========================================================================
// buildSpell() - 
//===========================================================================
int buildSpell(Spell *s, int startAngle)
{
    if (s->phonemes[0]) {
        createSpell(s, startAngle);
    }
    if (!s->tokens[0]) {
        s->tokens[0] = TOKEN_CREATE(SSHP_PAUSE, 0, 0);
        s->tokens[1] = 0;
    }
    return spellLen(s);
}

//===========================================================================
// checkSpells()
//===========================================================================
void checkSpells(Spell *slist, Motion *mlist, int mcnt)
{
    Motion *m, *mp;
    char *path;
    Spell *s, *best = 0, *best2=0;
    int scnt = 0;
    int startAngle = 0;

    //
    // ignore leading pause(s)
    //
    while(mlist && TOKEN_SHAPE(mlist->token) == MSHP_PAUSE) mlist = mlist->c_next;

    //
    // ignore trailing pause(s)
    //
    for (mp=m=mlist; m; m=m->c_next) {
        if (TOKEN_SHAPE(m->token) != MSHP_PAUSE) {
            mp = m;
        }
    }
    mp->c_next = 0;

    if (!mlist) return;

    //
    // find start angle
    //
#if 0
    for (m=mlist; m; m=m->c_next) {
        int shape = TOKEN_SHAPE(m->token);
        if (shape == MSHP_PULSE ||
            shape == MSHP_PULSE_PLUS) {
            startAngle = TOKEN_ANGLE(m->token);
            break;
        }
    }
#else
    startAngle = TOKEN_ANGLE(mlist->token);
#endif

    //
    // count len of max spell
    //
    for (s=slist; s->name; s++) {
        int len;
        len = buildSpell(s, startAngle);
        if (len > scnt) {
            scnt = len;
        }
    }

    scnt++;
    path = malloc((mcnt+1) * scnt);

    for (s=slist; s->name; s++) {
        memset(path,0,mcnt*scnt);
        scoreSpell(s,mlist,path,-1);
        if (!OPT_silent) {
            //
            // 2nd time with printing
            //
            scoreSpell(s,mlist,path,2);
            scoreSpell(s,mlist,path,3);
        }
        if (!best || best->score > s->score) {
            best = s;
        }
    }
    free(path);

    //
    // find 2nd best
    //
    best2 = 0;
    for (s=slist; s->name; s++) {
        if (s==best) continue;
        if (!best2) best2 = s;
        if (best2->score > s->score) {
            best2 = s;
        }
    }

    printf("\nRESULT:Scores\n");
    for (s=slist; s->name; s++) {
        printf("RESULT: %s  %05d  %s  (%s)\n",
            s==best  ? "*":
            s==best2 ? "+":" ",
            s->score,
            s->name,
            s->phonemes ? s->phonemes : "");
    }
}

//===========================================================================
// main()
//===========================================================================
int main(int argc, char *argv[])
{
    Spell *slist = spells;
    Spell *s;
    Motion mlist[CNT_MAX];
    int mcnt=0;
    char **argp;

    memset(mlist,0,sizeof(mlist));

    for(argp=&argv[1]; *argp; argp++) {
        if (!strcmp(*argp,"-s")) {
            OPT_silent=1;
        } else if (!strcmp(*argp,"-q")) {
            OPT_quick=1;
        } else {
            for (s=slist; s->name; s++) {
                if (!strcmp(*argp,s->name)) {
                    slist = s;
                    s[1].name = 0;
                    break;
                }
            }
        }
    }

    mcnt = get(stdin,mlist,mlist+(sizeof(mlist)/sizeof(mlist[0])));
    if (mcnt) {
        initialPrint(mlist);
        look(mlist,mcnt);

        if (!OPT_quick) {
            checkSpells(slist, mlist,mcnt);
        }
    }
    return 0;
}


This file Copyright (C) 2006 by Nathan (Acorn) Pooley
Go to TOP Wand page
Go to Acorn's personal webpage
Go to Hogwarts website: www.gotohogwarts.com
Snout: www.snout.org/game
Gadgets of Justice Unlimited
Snout GC (Wiki)
Snout Wiki
File created by do_doc at Wed May 30 03:26:56 PDT 2007