Trivial Solutions
                    The LoseThos 64-bit PC Operating System
File:/LT/Demo/GameStarters/RawHide.CPZ
This, and all LoseThos files, are public domain.  Do whatever you like.
God is watching; God is just; God is never out-done in generosity.  He talks!

LoseThos code compiles with the 64-bit LoseThos compiler/assembler I wrote.
Boot the LoseThos CD and you can compile it.  Not in Kansas anymore!

Click here for preliminary compiler information you need.

//This uses fixed-point.

AcctRegSetDftEntry("LT/RawHide","F64 best_score=9999;\r\n");
AcctRegExecuteBranch("LT/RawHide");

F64 t0,tf;

#define MAP_WIDTH       2048
#define MAP_HEIGHT      2048
#define MAP_BORDER      3
GrBitMap *base2;


#define GE_COW          0


<1>/* Graphics Not Rendered in HTML */
#define GE_BULL         1


<2>/* Graphics Not Rendered in HTML */
#define GE_HORSE        2


<3>/* Graphics Not Rendered in HTML */
#define GE_NUM          3



class RHGrElems
{
  U8 *img;
  I64 min_x,max_x,min_y,max_y;
} rh_grelems[GE_NUM];



#define NUM_ANIMALS     512

class Animal
{
  I64 num,x,y,dx,dy;
  RHGrElems *ge;
  U32 buddy;
  U8 type;
  BoolI8 dead,pad[2];
} *a;


// ************************************
#define COURSE_LEN      (MAP_HEIGHT*2)
I32 *rx,*ry,*rw;
I64 river_lo,river_hi;
U0 RiverNew()
{
  rx=MAlloc(COURSE_LEN*sizeof(I32));
  ry=MAlloc(COURSE_LEN*sizeof(I32));
  rw=MAlloc(COURSE_LEN*sizeof(I32));
  river_lo=river_hi=COURSE_LEN/2;
}

U0 RiverMake()
{
  I64 i,x,y,dx,dy,w,old_x,old_y;
  //Plan lower course of river
  old_x=-1;old_y=-1;
  x=MAP_WIDTH<<32/2; y=MAP_HEIGHT<<32/2;
  dx=0; dy=1<<32;
  w=20<<32;
  i=COURSE_LEN/2;
  while (y.i32[1]<MAP_HEIGHT-MAP_BORDER && i<COURSE_LEN) {
    if (old_x.i32[1]!=x.i32[1]||old_y.i32[1]!=y.i32[1]) {
      rx[i]=x.i32[1]; ry[i]=y.i32[1]; rw[i]=w.i32[1];
      river_hi=i++;
      old_x=x; old_y=y;
    }
    dx=LimitI64(dx+RandI32/64,-MAX_I32,MAX_I32);
    dy=LimitI64(dy+RandI32/64,0,MAX_I32);
    x+=dx; y+=dy;
    w=LimitI64(w+RandI32*2,10*MAX_U32,150*MAX_U32);
  }
  //Plan upper course of river
  old_x=-1;old_y=-1;
  x=MAP_WIDTH<<32/2; y=MAP_HEIGHT<<32/2;
  dx=0; dy=-1<<32;
  w=20<<32;
  i=COURSE_LEN/2-1;
  while (y.i32[1]>=MAP_BORDER && i>=0) {
    if (old_x.i32[1]!=x.i32[1]||old_y.i32[1]!=y.i32[1]) {
      rx[i]=x.i32[1]; ry[i]=y.i32[1]; rw[i]=w.i32[1];
      river_lo=i--;
      old_x=x; old_y=y;
    }
    dx=LimitI64(dx+RandI32/64,-MAX_I32,MAX_I32);
    dy=LimitI64(dy+RandI32/64,-MAX_I32,0);
    x+=dx; y+=dy;
    w=LimitI64(w+RandI32*2,10*MAX_U32,150*MAX_U32);
  }

  //Plot sand bar
  x=0; y=0;
  for (i=river_lo;i<=river_hi;i++) {
    base2->color=YELLOW;
    base2->pen_width=rw[i]+5;
    GrPlot3(base2,rx[i]+x.i32[1],ry[i]+y.i32[1],0);

    x=LimitI64(x+RandI32,-6*MAX_U32,6*MAX_U32);
    y=LimitI64(y+RandI32,-6*MAX_U32,6*MAX_U32);
  }

  //Plot water
  for (i=river_lo;i<=river_hi;i++) {
    base2->color=BLUE;
    base2->pen_width=rw[i];
    GrPlot3(base2,rx[i],ry[i],0);
  }
}

U0 RiverDel()
{
  Free(rx);
  Free(ry);
  Free(rw);
}


// ************************************
class RiverDrop
{
  RiverDrop *next,*last;
  I64 index,dx,dy;
} rd_root;
BoolI8 rd_lock;

U0 RiverDropsDel()
{
  RiverDrop *tempr,*tempr1;
  while (LBts(&rd_lock,0))
    Yield;
  tempr=rd_root.next;
  while (tempr!=&rd_root) {
    tempr1=tempr->next;
    RemQue(tempr);
    Free(tempr);
    tempr=tempr1;
  }
  rd_root.next=rd_root.last=&rd_root;
  LBtr(&rd_lock,0);
}

U0 RiverDropsNext(TaskStruct *mem_task)
{
  RiverDrop *tempr,*tempr1;
  while (LBts(&rd_lock,0))
    Yield;
  tempr=rd_root.next;
  while (tempr!=&rd_root) {
    tempr1=tempr->next;
    if (++tempr->index>river_hi) {
      RemQue(tempr);
      Free(tempr);
    } else {
      do {
        if (RandU16&1 &&
            GrPeek(base2,rx[tempr->index]+tempr->dx,ry[tempr->index]+tempr->dy)==BLUE)
          break;
        tempr->dx=LimitI64(tempr->dx+RandU16%3-1,-rw[tempr->index]/2,rw[tempr->index]/2);
        tempr->dy=LimitI64(tempr->dy+RandU16%3-1,-rw[tempr->index]/2,rw[tempr->index]/2);
      } while (GrPeek(base2,rx[tempr->index]+tempr->dx,ry[tempr->index]+tempr->dy)!=BLUE &&
               GrPeek(base2,rx[tempr->index]          ,ry[tempr->index])          ==BLUE); //Might be reinitializing
    }
    tempr=tempr1;
  }
  tempr=MAlloc(sizeof(RiverDrop),mem_task);
  tempr->index=river_lo;
  tempr->dx=0;
  tempr->dy=0;
  InsQue(tempr,rd_root.last);
  LBtr(&rd_lock,0);
}

U0 RiverDropsDraw(GrBitMap *base,I64 cx,I64 cy)
{
  RiverDrop *tempr;
  base->color=LTBLUE;
  while (LBts(&rd_lock,0))
    Yield;
  tempr=rd_root.next;
  while (tempr!=&rd_root) {
    GrPlot(base,rx[tempr->index]+tempr->dx-cx,ry[tempr->index]+tempr->dy-cy);
    tempr=tempr->next;
  }
  LBtr(&rd_lock,0);
}

// ************************************
U0 UpdateWin(TaskStruct *task)
{
  static I64 last_ipx=0;
  static BoolI8 left=TRUE;
  F64 t;
  I64 i,
     cx=(MAP_WIDTH -task->win_pixel_width)/2,
     cy=(MAP_HEIGHT-task->win_pixel_height)/2;
  if (task->win_scroll_x+cx<0)
    task->win_scroll_x=-cx;
  if (task->win_scroll_x+cx>MAP_WIDTH-task->win_pixel_width)
    task->win_scroll_x=MAP_WIDTH-task->win_pixel_width-cx;
  if (task->win_scroll_y+cy<0)
    task->win_scroll_y=-cy;
  if (task->win_scroll_y+cy>MAP_HEIGHT-task->win_pixel_height)
    task->win_scroll_y=MAP_HEIGHT-task->win_pixel_height-cy;

  GrBitMap *base=GrAlias(gr_refreshed_base,task);
  GrBlot(base,-cx,-cy,base2);

  RiverDropsDraw(base,cx,cy);

  for (i=0;i<NUM_ANIMALS;i++)
    if (!a[i].dead) {
      if (a[i].dx<0) {
        base->flags|=BMF_JUST_MIRROR|BMF_SYMMETRY;
        GrSetSymmetry(base,a[i].x.i32[1]-cx,0,a[i].x.i32[1]-cx,1);
      }
      GrElemsPlot3(base,a[i].x.i32[1]-cx,a[i].y.i32[1]-cy,0,a[i].ge->img);
      base->flags&=~(BMF_JUST_MIRROR|BMF_SYMMETRY);
    }


  if (ipx-last_ipx>0)
    left=FALSE;
  else if (ipx-last_ipx<0)
    left=TRUE;
  if (left) {
    base->flags|=BMF_JUST_MIRROR|BMF_SYMMETRY;
    GrSetSymmetry(base,ipx-task->win_pixel_left-task->win_scroll_x,0,ipx-task->win_pixel_left-task->win_scroll_x,1);
  }
  GrElemsPlot3(base,
    ipx-task->win_pixel_left-task->win_scroll_x,
    ipy-task->win_pixel_top -task->win_scroll_y,0,rh_grelems[GE_HORSE].img);
  base->flags&=~(BMF_JUST_MIRROR|BMF_SYMMETRY);
  last_ipx=ipx;

  if (tf) {
    base->color=RED;
    t=tf-t0;
    if (Blink(,tP(task)))
      GrPutS(base,(task->win_pixel_width-FONT_WIDTH*9)>>1-task->win_scroll_x,
                  (task->win_pixel_height-FONT_HEIGHT)>>1-task->win_scroll_y,
        "Game Over");
  } else {
    base->color=BLACK;
    t=tP(task)-t0;
  }
  GrPrintF(base,-task->win_scroll_x,-task->win_scroll_y,"Time:%7.2fs  Best:%7.2fs",t,best_score);
  GrDel(base);
}


U0 BuddySelect(I64 i)
{
  I64 b,best_b=i,score,best_score=MAX_I64;
  for (b=0;b<NUM_ANIMALS;b++) {
    if (b!=i && !a[b].dead) {
      score=128*128+RandU32%(512*512)+
        SqrI64(a[b].x.i32[1]-a[i].x.i32[1])+
        SqrI64(a[b].y.i32[1]-a[i].y.i32[1]);
      if (score<best_score) {
        best_score=score;
        best_b=b;
      }
    }
  }
  a[i].buddy=best_b;
}

U0 AnimateTask(TaskStruct *parent)
{
  I64 i,cx,cy,cursor_x,cursor_y,dd,ddx,ddy,cnt,max_speed=MAX_I64,
     min_x,max_x,min_y,max_y,updates=0;
  F64 f,d,dx,dy,s,stress;
  Animal *tempa,*tempa1;
  while (TRUE) {
    max_speed=LimitU64(max_speed,MAX_U32/3,200*MAX_U32);
    cx=(MAP_WIDTH -parent->win_pixel_width)/2,
    cy=(MAP_HEIGHT-parent->win_pixel_height)/2;
    cursor_x=ipx+cx-parent->win_pixel_left-parent->win_scroll_x;
    cursor_y=ipy+cy-parent->win_pixel_top -parent->win_scroll_y;
    min_x=max_x=min_y=max_y=cnt=0;
    stress=0;
    for (i=0;i<NUM_ANIMALS;i++) {
      tempa=&a[i];
      if (!tempa->dead) {
//Move away from horse
        ddx=tempa->x.i32[1]-cursor_x;
        ddy=tempa->y.i32[1]-cursor_y;
        if (dd=SqrI64(ddx)+SqrI64(ddy)) {
          d=Sqrt(dd);
          dx=ddx/d;
          dy=ddy/d;
          f=1.0e2*MAX_U32/dd;
          tempa->dx+=f*dx;
          tempa->dy+=f*dy;
        }

//Reselect buddy about every NUM_ANIMALS*10ms=5.12 seconds
        tempa1=&a[tempa->buddy];
        if (tempa1->dead || i==updates%NUM_ANIMALS) {
          BuddySelect(i);
          tempa1=&a[tempa->buddy];
        }

//Move toward buddy
        ddx=tempa->x.i32[1]-tempa1->x.i32[1];
        ddy=tempa->y.i32[1]-tempa1->y.i32[1];
        if (dd=SqrI64(ddx)+SqrI64(ddy)) {
          d=Sqrt(dd);
          s=d`1.25-80;
          stress+=Abs(s);
          dx=ddx/d;
          dy=ddy/d;
          f=-0.001*s*MAX_U32;
          tempa->dx+=f*dx;
          tempa->dy+=f*dy;
        }

//Make velocity similar to buddy
        tempa->dx+=0.1*(tempa1->dx-tempa->dx);
        tempa->dy+=0.1*(tempa1->dy-tempa->dy);


//Add random movement, limit speed and dampen speed
        tempa->dx=0.995*LimitI64(tempa->dx+RandI32/32,-max_speed,max_speed);
        tempa->dy=0.995*LimitI64(tempa->dy+RandI32/32,-max_speed,max_speed);

//Slow in river
        if (GrPeek(base2,tempa->x.i32[1],tempa->y.i32[1])!=LTGREEN) {
          tempa->dx/=4;
          tempa->dy/=4;
        }

        tempa->x+=tempa->dx;
        tempa->y+=tempa->dy;

//Keep on map
        if (!(MAP_BORDER-tempa->ge->min_x<=tempa->x.i32[1]<MAP_WIDTH-MAP_BORDER-tempa->ge->max_x)) {
          tempa->x -=tempa->dx;
          tempa->dx=-tempa->dx;
        }
        if (!(MAP_BORDER-tempa->ge->min_y<=tempa->y.i32[1]<MAP_HEIGHT-MAP_BORDER-tempa->ge->max_y)) {
          tempa->y -=tempa->dy;
          tempa->dy=-tempa->dy;
        }

        if (cnt) {
          min_x=MinI64(min_x,tempa->x);
          max_x=MaxI64(max_x,tempa->x);
          min_y=MinI64(min_y,tempa->y);
          max_y=MaxI64(max_y,tempa->y);
        } else {
          min_x=max_x=tempa->x;
          min_y=max_y=tempa->y;
        }

        cnt++;
      }
    }
    if (!(updates&15))
      RiverDropsNext(parent);

    if (!tf && (max_x-min_x)>>32<GR_WIDTH && (max_y-min_y)>>32<GR_HEIGHT) {
      tf=tP(parent);
      music_mute=TRUE;
      Snd(2000);Sleep(200);Snd(0);Sleep(100);
      if (tf-t0<best_score) {
        best_score=tf-t0;
        Snd(2000);Sleep(200);Snd(0);Sleep(100);
      }
      music_mute=FALSE;
    }

    updates++;

    if (cnt)
      stress/=cnt;
    else
      stress=0;
    if (stress>100.0) {
      Yield;
      max_speed=stress/5.0*MAX_U32; //Converge faster at start-up
    } else {
      Sleep(10);
      max_speed=0; //Will be set to normal max speed
    }
  }
}

U0 SongTask(U64 dummy=0)
{ //Randomly generated (by God :-)
  nounusedwarn dummy;
  Fs->task_end_cb=&SndTaskEndCB;
  MusicSettingsRst;
  while (TRUE) {
        Play("W13qC2etG!3DCBDC!!ECFqFC!sADAD!qB");
        Play("C2etG!3DCBDC!!ECFqFC!sADAD!qB");
        Play("!2sG3A!2G3A!qG2etG3GDeBBqBFeBAqE");
        Play("!2sG3A!2G3A!qG2etG3GDeBBqBFeBAqE");
  }
}


U0 ReInit()
{
  I64 i;

  RiverDropsDel;
  base2->color=LTGREEN;
  GrRect(base2,2,2,MAP_WIDTH-4,MAP_HEIGHT-4);

  RiverMake;

  base2->pen_width=MAP_BORDER;
  base2->color=RED;
  GrLineRect4(base2,MAP_BORDER/2,MAP_BORDER/2,MAP_WIDTH-(MAP_BORDER+1)/2,MAP_HEIGHT-(MAP_BORDER+1)/2);

  for (i=river_lo;i<=river_hi;i++)
    RiverDropsNext(Fs);

  MemSet(a,0,NUM_ANIMALS*sizeof(Animal));
  for (i=0;i<NUM_ANIMALS;i++) {
    a[i].num=i;
    a[i].x=(64+RandU32%(MAP_WIDTH-128))<<32;
    a[i].y=(64+RandU32%(MAP_WIDTH-128))<<32;
    if (i&1)
      a[i].ge=&rh_grelems[GE_COW];
    else
      a[i].ge=&rh_grelems[GE_BULL];
    BuddySelect(i);
  }
  t0=tP;
  tf=0;
}

U0 Init()
{
  I64 i;
  RHGrElems *ge;

  rh_grelems[0].img=<1>;
  rh_grelems[1].img=<2>;
  rh_grelems[2].img=<3>;
  for (i=0;i<GE_NUM;i++) {
    ge=&rh_grelems[i];
    GrElemsExtents(ge->img,&ge->min_x,&ge->max_x,&ge->min_y,&ge->max_y);
  }

  RiverNew;
  rd_lock=0;
  rd_root.next=rd_root.last=&rd_root;
  base2=GrNew(BMT_COLOR4,MAP_WIDTH,MAP_HEIGHT);
  a=MAlloc(NUM_ANIMALS*sizeof(Animal));
  ReInit;
}

U0 CleanUp()
{
  GrDel(base2);
  Free(a);
  RiverDropsDel;
  RiverDel;
}

U0 RawHide()
{
  I64 msg_code,p1,p2;
  SettingsPush; //See SettingsPush
  PopUpOk(
        "Coral the cattle into an area\r\n"
        "the size of a screen.\r\n\r\n"
        "Scroll with {CTRL-Left Grab}.\r\n"
        "Center with {CTRL-Right}.");
  MenuPush(
"File {"
"  Abort(,CH_SHIFT_ESC);"
"  Exit(,CH_ESC);"
"}"
"Play {"
"  Restart(,CH_CR);"
"}"
);
  Fs->song_task=Spawn(&SongTask,NULL,"Song",Fs);
  Fs->win_inhibit=WIF_ALL-WIF_BORDER-WIF_GRAB_SCROLL-WIF_MENU;
  WinMax;
  WordStat(OFF);
  Preempt(OFF);
  Init;
  Fs->animate_task=Spawn(&AnimateTask,Fs,"Animate",Fs);
  Fs->update_win=&UpdateWin;
  try {
    while (TRUE) {
      msg_code=GetMsg(&p1,&p2,1<<MSG_KEY_DOWN+1<<MSG_IP_L_DOWN+1<<MSG_IP_R_DOWN);
      switch (msg_code) {
        case MSG_IP_L_DOWN:  //Doesn't do anything, yet.
          break;
        case MSG_IP_R_DOWN:  //Doesn't do anything, yet.
          break;
        case MSG_KEY_DOWN:
          switch (p1) {
            case CH_CR:
              ReInit;
              break;
            case CH_SHIFT_ESC:
            case CH_ESC:
              goto rh_done;
          }
          break;
      }
    }
rh_done:
  } catch
    CatchAll;
  MenuPop;
  SettingsPop;
  CleanUp;
  AcctRegWriteBranch("LT/RawHide","F64 best_score=%5.4f;\r\n",best_score);
}

RawHide;
 
Historical capture: 2011-10-22. Home | Power on LoseThos | Sources and changes