This is SokoView.m in view mode; [Download] [Up]
#import "SokoView.h"
@implementation SokoView
- setSokoWindow:sender
{
sokoWindow=sender;
return self;
}
- setWaitPanel:sender
{
waitPanel=sender;
return self;
}
- initFrame:(const NXRect *)rect;
{
[super initFrame:rect];
[self allocateGState];
[[super window] useOptimizedDrawing:YES];
[[super window] addToEventMask:NX_RMOUSEDOWN];
fieldX =10;
fieldY =10;
winOverhead.width =2.0; // good approx.
winOverhead.height =23.0; // will be corrected at runtime
way =calloc(1,sizeof(char));
east =calloc(1,sizeof(int));
west =calloc(1,sizeof(int));
north =calloc(1,sizeof(int));
south =calloc(1,sizeof(int));
wayDescription =calloc(1,sizeof(int));
knownWays =calloc(1,sizeof(int));
testWays =calloc(1,sizeof(int));
waysToTest =calloc(1,sizeof(int));
background = [[[NXImage allocFromZone:[self zone]] init] setScalable:NO];
[background useDrawMethod:@selector(drawBackground:) inObject:self];
women = [[[NXImage allocFromZone:[self zone]] init] setScalable:NO];
[women useDrawMethod:@selector(drawWomen:) inObject:self];
brick = [[[NXImage allocFromZone:[self zone]] init] setScalable:NO];
[brick useDrawMethod:@selector(drawBrick:) inObject:self];
gold = [[[NXImage allocFromZone:[self zone]] init] setScalable:NO];
[gold useDrawMethod:@selector(drawGold:) inObject:self];
base = [[[NXImage allocFromZone:[self zone]] init] setScalable:NO];
[base useDrawMethod:@selector(drawBase:) inObject:self];
return self;
}
- readLevelFromFile:(char *)level
{
FILE *fd;
char filename[MAXPATHLEN];
int i,ix,iy;
int minx=MAXGAMESIZE_X;
int miny=MAXGAMESIZE_Y;
int maxx=0;
int maxy=0;
goldN=0;baseN=0,solved=0;initSolved=0;
strcpy(filename,NXArgv[0]);
filename[strlen(filename)-strlen([NXApp appName])]=0;
sprintf(filename,"%sLevels/screen.%s",filename,level);
if((fd=fopen(filename,"r"))==NULL)
{
sprintf(filename,"%s/Levels/screen.%s",getwd(filename),level);
if((fd=fopen(filename,"r"))==NULL)
if(atoi(level)>50)
NXRunAlertPanel("Congratulations !","You solved all Levels - you can mail me for new Levels","Quit",NULL,NULL);
else
NXRunAlertPanel("File not found Error",filename,"Quit",NULL,NULL);
[NXApp terminate:self];
}
for(i=0;i<MAXGAMESIZE_X*MAXGAMESIZE_Y;i++)field[i]=ILLEGAL;
fieldX=0;
fieldY=0;
while(!feof(fd) && fieldY<MAXGAMESIZE_Y)
fgets(field+((fieldY++)*MAXGAMESIZE_X),MAXGAMESIZE_X,fd);
fclose(fd);
for(iy=0;iy<fieldY;iy++)
for(ix=0;ix<MAXGAMESIZE_X;ix++)
if(field[iy*MAXGAMESIZE_X+ix]==BRICK)
{
if(ix<minx) minx=ix;
if(iy<miny) miny=iy;
if(ix>maxx) maxx=ix;
if(iy>maxy) maxy=iy;
}
fieldX=maxx-minx+1;
fieldY=maxy-miny+1;
for(iy=0;iy<fieldY;iy++)
{
for(ix=0;ix<fieldX;ix++)
{
field[iy*fieldX+ix] =field[((miny+iy)*MAXGAMESIZE_X)+minx+ix];
if(field[iy*fieldX+ix]==WOMEN || field[iy*fieldX+ix]==W_O_B) womenPosition =ix+iy*fieldX;
if(field[iy*fieldX+ix]==GOLD) goldPosition[goldN++] =ix+iy*fieldX;
if(field[iy*fieldX+ix]==G_O_B) { goldPosition[goldN++] =ix+iy*fieldX; initSolved++; }
if(field[iy*fieldX+ix]==BASE || field[iy*fieldX+ix]==G_O_B || field[iy*fieldX+ix]==W_O_B) basePosition[baseN++] =ix+iy*fieldX;
}
}
solved=initSolved;
sprintf(filename,"Sokoban - Level : %s",level);
[[super window] setTitle:filename];
[self sizeTo:bounds.size.width :bounds.size.height];
[self display];
NXPing();
[waitPanel makeKeyAndOrderFront:self];
[waitPanel display];
NXPing();
[self setupWayfinder];
[self savePosition];
[waitPanel orderOut:self];
return self;
}
- restartLevel
{
int p;
goldN=0;baseN=0,solved=initSolved;
for(p=0;p<pieces;p++)
{
if(field[p]==WOMEN || field[p]==W_O_B) womenPosition =p;
if(field[p]==GOLD || field[p]==G_O_B) goldPosition[goldN++] =p;
if(field[p]==BASE || field[p]==G_O_B || field[p]==W_O_B) basePosition[baseN++] =p;
}
//[self savePosition]; this was yodas idea not mine !
[self display];
return self;
}
- restorePosition
{
solved =last_solved;
womenPosition =last_womenPosition;
bcopy(last_goldPosition,goldPosition,goldN*sizeof(int));
bcopy(last_basePosition,basePosition,baseN*sizeof(int));
[self display];
return self;
}
- savePosition
{
last_solved =solved;
last_womenPosition =womenPosition;
bcopy(goldPosition,last_goldPosition,goldN*sizeof(int));
bcopy(basePosition,last_basePosition,baseN*sizeof(int));
return self;
}
- setupWayfinder
{
int p,q,k,r;
int test;
pieces=fieldX*fieldY;
free(way);
free(east);
free(west);
free(north);
free(south);
free(wayDescription);
free(knownWays);
free(testWays);
free(waysToTest);
way =calloc(pieces*pieces,sizeof(char));
east =calloc(pieces,sizeof(int));
west =calloc(pieces,sizeof(int));
north =calloc(pieces,sizeof(int));
south =calloc(pieces,sizeof(int));
wayDescription =calloc(pieces,sizeof(int));
knownWays =calloc(pieces,sizeof(int));
testWays =calloc(pieces,sizeof(int));
waysToTest =calloc(pieces,sizeof(int));
cway =calloc(pieces*pieces,sizeof(char));
for(p=0;p<pieces;p++)
{
if(field[p]!=BRICK)
{
way[p*pieces+p]=MYWAY;
test =(p/fieldX)*fieldX+((p%fieldX+1)%fieldX);if(field[test]!=BRICK){way[p*pieces+test]=EAST;east[p]=test;}else east[p]=-1;
test =(p/fieldX)*fieldX+((p-1+fieldX)%fieldX);if(field[test]!=BRICK){way[p*pieces+test]=WEST;west[p]=test;}else west[p]=-1;
test =(p-fieldX +pieces)%pieces; if(field[test]!=BRICK){way[p*pieces+test]=NORTH;north[p]=test;}else north[p]=-1;
test =(p+fieldX +pieces)%pieces; if(field[test]!=BRICK){way[p*pieces+test]=SOUTH;south[p]=test;}else south[p]=-1;
}
}
bcopy(way,cway,pieces*pieces);
do
{
test=FALSE;
for(p=0;p<pieces;p++) // p=piece,q=question,k=known
{
r=p*pieces;
if(field[p]!=BRICK)
{
if((q=east[p])!=-1)
{
for(k=0;k<pieces;k++)
if(way[r+k]==NOWAY && way[q*pieces+k]!=NOWAY)
test=cway[r+k]=EAST;
}
if((q=west[p])!=-1)
{
for(k=0;k<pieces;k++)
if(way[r+k]==NOWAY && way[q*pieces+k]!=NOWAY)
test=cway[r+k]=WEST;
}
if((q=north[p])!=-1)
{
for(k=0;k<pieces;k++)
if(way[r+k]==NOWAY && way[q*pieces+k]!=NOWAY)
test=cway[r+k]=NORTH;
}
if((q=south[p])!=-1)
{
for(k=0;k<pieces;k++)
if(way[r+k]==NOWAY && way[q*pieces+k]!=NOWAY)
test=cway[r+k]=SOUTH;
}
}
}
for(p=0;p<pieces*pieces;p+=pieces)
if(field[p/pieces]!=BRICK)
bcopy(cway+p,way+p,pieces);
}
while(test);
free(cway);
return self;
}
- (int)findWay:(int)end
{
return way[womenPosition*pieces+end];
}
- findWayWithoutConflicts:(int)end
{
int i;
int mom,moves;
int *tw,*wtt;
int *beg_tw,*beg_wtt,*beg_mom;
memset(knownWays,0xff,pieces*sizeof(int));
for(i=0;i<goldN;i++) // removing goldbars from possible ways
knownWays[goldPosition[i]]=-2;
knownWays[womenPosition]=-2;
waysToTest[0] =womenPosition;
waysToTest[1] =-1;
beg_tw =testWays;
beg_wtt =waysToTest;
moves =0;
do
{
beg_mom =beg_wtt;
beg_wtt =beg_tw;
beg_tw =beg_mom;
beg_wtt[0]=-1;
tw =beg_tw;
wtt =beg_wtt;
do
{
if( (mom=east[*tw])!=-1 && knownWays[mom]==-1) knownWays[*wtt++=mom]=*tw;
if( (mom=west[*tw])!=-1 && knownWays[mom]==-1) knownWays[*wtt++=mom]=*tw;
if( (mom=north[*tw])!=-1 && knownWays[mom]==-1) knownWays[*wtt++=mom]=*tw;
if( (mom=south[*tw])!=-1 && knownWays[mom]==-1) knownWays[*wtt++=mom]=*tw;
}
while(end!=*tw && *++tw!=-1);
*wtt=-1;
moves++;
}
while(end!=*tw && wtt!=beg_wtt);
if(end!=*tw)
{
wayDescription[0]=-1;
return self;
}
mom=*tw;
while(moves)
{
wayDescription[--moves]=mom;
mom=knownWays[mom];
}
return self;
}
- (int)findPositionFromDirection:(int)begin :(int)direction
{
int newPosition;
switch(direction)
{
case EAST :newPosition=east[begin];break;
case WEST :newPosition=west[begin];break;
case NORTH :newPosition=north[begin];break;
case SOUTH :newPosition=south[begin];break;
default :newPosition=begin;
}
if(newPosition==-1) return begin;
return newPosition;
}
/****************************************************animation */
- moveWomen:(int)end
{
int direction;
int newPosition,behind;
int flag,mom;
int isBase,wasBase;
int firstMove=TRUE;
do
{
if((direction=[self findWay:end])==NOWAY)
{
NXBeep();
return self;
}
newPosition=[self findPositionFromDirection:womenPosition :direction];
if(newPosition==womenPosition)
{
NXBeep();
return self;
}
flag=-1;
wasBase=0;
isBase=0;
for(mom=0;mom<goldN;mom++)
{
if(goldPosition[mom]==newPosition) flag=mom;
if(basePosition[mom]==newPosition) wasBase=1;
}
if(flag!=-1)
{
behind=[self findPositionFromDirection:newPosition :direction];
for(mom=0;mom<goldN;mom++)
{
if(goldPosition[mom]==behind) flag=-2;
if(basePosition[mom]==behind) isBase=1;
}
if(flag==-2)
{
NXBeep();
return self;
}
if(firstMove)
{
[self savePosition];
firstMove=FALSE;
}
[self animateWomenWithGold:newPosition :behind];
goldPosition[flag]=behind;
solved=solved+isBase-wasBase;
if(end==behind) return self;
}
else
{
[self animateWomen:newPosition];
}
}
while(newPosition!=end);
return self;
}
- animateWomen:(int)newPosition
{
NXRect rect={0.0,0.0,matrix.width,matrix.height};
[self lockFocus];
rect.origin.x=(float)( (womenPosition%fieldX)*matrix.width );
rect.origin.y=(bounds.size.height-matrix.height)-(float)( (womenPosition/fieldX)*matrix.height );
[background composite:NX_COPY fromRect:&rect toPoint:&rect.origin];
rect.origin.x=(float)( (newPosition%fieldX)*matrix.width );
rect.origin.y=(bounds.size.height-matrix.height)-(float)( (newPosition/fieldX)*matrix.height );
[women composite:NX_SOVER toPoint:&rect.origin];
NXPing();
usleep(300);
[self unlockFocus];
womenPosition=newPosition;
return self;
}
- animateWomenWithGold:(int)newPosition :(int)behind
{
NXRect rect={0.0,0.0,matrix.width,matrix.height};
[self lockFocus];
rect.origin.x=(float)( (newPosition%fieldX)*matrix.width );
rect.origin.y=(bounds.size.height-matrix.height)-(float)( (newPosition/fieldX)*matrix.height );
[background composite:NX_COPY fromRect:&rect toPoint:&rect.origin];
rect.origin.x=(float)( (behind%fieldX)*matrix.width );
rect.origin.y=(bounds.size.height-matrix.height)-(float)( (behind/fieldX)*matrix.height );
[gold composite:NX_SOVER toPoint:&rect.origin];
rect.origin.x=(float)( (newPosition%fieldX)*matrix.width );
rect.origin.y=(bounds.size.height-matrix.height)-(float)( (newPosition/fieldX)*matrix.height );
[women composite:NX_SOVER toPoint:&rect.origin];
rect.origin.x=(float)( (womenPosition%fieldX)*matrix.width );
rect.origin.y=(bounds.size.height-matrix.height)-(float)( (womenPosition/fieldX)*matrix.height );
[background composite:NX_COPY fromRect:&rect toPoint:&rect.origin];
NXPing();
[self unlockFocus];
womenPosition=newPosition;
return self;
}
/********************************************************** drawing the Images */
- drawBackground:imageRep
{
int ix,iy;
NXPoint ori;
NXRect rect={0.0,0.0,matrix.width,matrix.height};
PSsetgray(NX_DKGRAY);
NXRectFill(&bounds);
for(iy=0;iy<fieldY;iy++)
{
for(ix=0;ix<fieldX;ix++)
{
ori.x=(float)ix*matrix.width;
ori.y=(bounds.size.height-matrix.height)-(float)iy*matrix.height;
if(field[iy*fieldX+ix]==BRICK)
[brick composite:NX_COPY fromRect:&rect toPoint:&ori];
if(field[iy*fieldX+ix]==BASE ||field[iy*fieldX+ix]==W_O_B || field[iy*fieldX+ix]==G_O_B)
[base composite:NX_COPY fromRect:&rect toPoint:&ori];
}
}
return self;
}
- drawWomen:imageRep
{
NXRect rect={0.0,0.0,matrix.width,matrix.height};
#define RADIUS 8.0 // Ball radius
#define BALLWIDTH (RADIUS * 2.0) // Ball width
#define BALLHEIGHT (RADIUS * 2.0) // Ball height
#define SHADOWOFFSET 3.0
PSsetalpha(0.0);
NXRectFill(&rect);
PSsetalpha(1.0);
PSscale (matrix.width / BALLWIDTH, matrix.height / BALLHEIGHT);
PSarc (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET-1.0, 0.0, 360.0);
PSsetgray (NX_LTGRAY);
PSfill ();
PSarcn(RADIUS-SHADOWOFFSET/2 , RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET-3.0, 170.0, 100.0);
PSarc (RADIUS-SHADOWOFFSET/2 , RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET-2.0, 100.0, 170.0);
PSsetgray (NX_WHITE);
PSfill ();
PSarcn(RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET-2.0, 350.0, 280.0);
PSarc (RADIUS-SHADOWOFFSET/2, RADIUS+SHADOWOFFSET/2, RADIUS-SHADOWOFFSET-2.0, 280.0, 350.0);
PSsetgray (NX_DKGRAY);
PSfill ();
return self;
}
- drawBrick:imageRep
{
NXRect rect={0.0,0.0,matrix.width,matrix.height};
NXDrawButton(&rect,NULL);
return self;
}
- drawGold:imageRep
{
NXRect rect={0.0,0.0,matrix.width,matrix.height};
PSsetalpha(0.0);
NXRectFill(&rect);
PSsetalpha(1.0);
rect.size.height=matrix.height*2/3;
rect.size.width =matrix.width*2/3;
rect.origin.x =matrix.width/6;
rect.origin.y =matrix.height/6;
NXDrawButton (&rect, NULL);
NXInsetRect (&rect, matrix.width/10, matrix.height/10);
NXDrawWhiteBezel (&rect, NULL);
return self;
}
- drawBase:imageRep
{
NXRect rect={0.0,0.0,matrix.width,matrix.height};
PSsetgray(NX_DKGRAY);
NXRectFill(&rect);
NXDrawWhiteBezel(&rect,NULL);
return self;
}
/******************* class Methods */
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (BOOL)acceptsFirstMouse
{
return NO;
}
- rightMouseDown:(NXEvent *)event
{
static NXPoint mouse;
static int posx,posy;
static int end;
static char str[10];
[sokoWindow getMouseLocation:&mouse];
[self convertPoint:&mouse fromView:nil];
posx=(int)floor(mouse.x/matrix.width);
posy=(int)floor((bounds.size.height-mouse.y)/matrix.height);
end=posx+posy*fieldX;
[self moveWomen:end];
if(solved==baseN)
{
solved=atoi((char *)NXGetDefaultValue([NXApp appName],"Level"));
sprintf(str,"%d",++solved);
NXWriteDefault([NXApp appName],"Level",str);
[self readLevelFromFile:str];
}
return self;
}
- mouseDown:(NXEvent *)event
{
static NXPoint mouse;
static int posx,posy;
static int end;
static char str[10];
static int *mom;
[sokoWindow getMouseLocation:&mouse];
[self convertPoint:&mouse fromView:nil];
posx=(int)floor(mouse.x/matrix.width);
posy=(int)floor((bounds.size.height-mouse.y)/matrix.height);
end=posx+posy*fieldX;
if(womenPosition==end) return self;
if([self findWay:end]==NOWAY)
{
NXBeep();
return self;
}
[self findWayWithoutConflicts:end];
if(*wayDescription==-1)
{
NXBeep();
return self;
}
mom=wayDescription;
do
{
[self animateWomen:*mom++];
}
while(end!=womenPosition);
if(solved==baseN)
{
solved=atoi((char *)NXGetDefaultValue([NXApp appName],"Level"));
sprintf(str,"%d",++solved);
NXWriteDefault([NXApp appName],"Level",str);
[self readLevelFromFile:str];
}
return self;
}
- keyDown:(NXEvent *)event
{
static char str[10];
if(event->data.key.charSet==NX_SYMBOLSET)
{
switch(event->data.key.charCode)
{
case 0xAD: [self moveWomen:[self findPositionFromDirection:womenPosition :NORTH]];break;
case 0xAE: [self moveWomen:[self findPositionFromDirection:womenPosition :EAST]];break;
case 0xAF: [self moveWomen:[self findPositionFromDirection:womenPosition :SOUTH]];break;
case 0xAC: [self moveWomen:[self findPositionFromDirection:womenPosition :WEST]];break;
}
}
if(event->data.key.charSet==NX_ASCIISET)
{
switch(event->data.key.charCode)
{
case '8': [self moveWomen:[self findPositionFromDirection:womenPosition :NORTH]];break;
case '6': [self moveWomen:[self findPositionFromDirection:womenPosition :EAST]];break;
case '2': [self moveWomen:[self findPositionFromDirection:womenPosition :SOUTH]];break;
case '4': [self moveWomen:[self findPositionFromDirection:womenPosition :WEST]];break;
case 'u': return [self restorePosition];
case 'U': return [self restorePosition];
case 'r': return [self restartLevel];
case 'R': return [self restartLevel];
}
}
if(solved==baseN)
{
solved=atoi((char *)NXGetDefaultValue([NXApp appName],"Level"));
sprintf(str,"%d",++solved);
NXWriteDefault([NXApp appName],"Level",str);
[self readLevelFromFile:str];
}
return self;
}
- sizeTo:(NXCoord)width :(NXCoord)height
{
NXRect rect;
width =floor(width);
height =floor(height);
matrix.width=floor(width/(float)fieldX);
matrix.height=floor(height/(float)fieldY);
if(matrix.width !=width/fieldX || matrix.height!=height/fieldY)
{
[sokoWindow getFrame:&rect];
winOverhead.width =rect.size.width-width;
winOverhead.height =rect.size.height-height;
return [sokoWindow sizeWindow:(matrix.width*(float)fieldX):(matrix.height*(float)fieldY)];
}
if(width==bounds.size.width && height==bounds.size.height)
{
[background lockFocus];
[self drawBackground:self];
[background unlockFocus];
}
[super sizeTo:width :height];
[women setSize:&matrix];
[brick setSize:&matrix];
[gold setSize:&matrix];
[base setSize:&matrix];
[background setSize:&bounds.size];
return self;
}
- drawSelf:(NXRect *)zone :(int)count
{
NXPoint ori;
int i;
[background setSize:&matrix]; // cursed background !
[background setSize:&bounds.size];
[background composite:NX_COPY fromRect:zone toPoint:&bounds.origin];
for(i=0;i<goldN;i++)
{
ori.x=(float)(goldPosition[i]%fieldX)*matrix.width;
ori.y=(bounds.size.height-matrix.height)-(float)(goldPosition[i]/fieldX)*matrix.height;
[gold composite:NX_SOVER toPoint:&ori];
}
ori.x=(float)(womenPosition%fieldX)*matrix.width;
ori.y=(bounds.size.height-matrix.height)-(float)(womenPosition/fieldX)*matrix.height;
[women composite:NX_SOVER toPoint:&ori];
return self;
}
/****************** delegated Methods */
- windowWillResize:sender toSize:(NXSize *)frameSize
{
frameSize->width =frameSize->width-winOverhead.width;
frameSize->height =frameSize->height-winOverhead.height;
frameSize->width =rint( frameSize->width / (float)fieldX -.5)*(float)fieldX;
frameSize->height =rint( frameSize->height / (float)fieldY -.5)*(float)fieldY;
if(frameSize->width <(float)(fieldX*10)) frameSize->width =(float)(fieldX*10);
if(frameSize->height<(float)(fieldY*10)) frameSize->height=(float)(fieldY*10);
frameSize->width =frameSize->width+winOverhead.width;
frameSize->height =frameSize->height+winOverhead.height;
return sender;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.