// // XMover: Modul zur Optimierten Ball-Darstellung unter XWindows // - flackerfreies Bewegen der Bälle durch Hilfspixmaps // - Beleuchtungseffekt durch Raytracing-Bitmaps // - Darstellung halber Kugeln durch vorberechnete Pixmaps // #ifndef _vec3_h # include "vec3.h" #endif #define LEFT_RIGHT_OPT #define USE_STUFFER /*----------------------------------------------------------------------------*/ PixmapCache::PixmapCache() { count = 0; } PixmapCache::~PixmapCache() { } Pixmap PixmapCache::Lock( int id_in, int size_in ) { for (int i=0;i=PCACHE_MAX) { fprintf(stderr,"internal error 2 in PixmapCache::InsertAndLock\n" ); return; } pmap[count] = pix; size[count] = size_in; id[count] = id_in; lockcount[count] = 1; count++; } PixmapCache PixmapCache::pcache; /*----------------------------------------------------------------------------*/ // // Klasse zur Komprimierung der RingState-Felder // class Stuffer { public: Stuffer() : ws(0), wl(sizeof(RingState)) {} void Init( int ws_in, int wl_in ) { ws = ws_in; if (wl!=wl_in) { printf( "ERROR: Stuffer used with illegal input size !\n" ); exit(0); } } void Shrink( RingState *addr, int len ); void Expand( RingState *addr, int len ); int SSize( int len ) #ifdef USE_STUFFER { return ((len*ws+7)/8); } #else { return ((len*wl+7)/8); } #endif int LSize( int len ) { return ((len*wl+7)/8); } private: int ws; // Bits klein const int wl; // Bits gross static int mtab[9]; }; int Stuffer::mtab[9] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; #ifdef USE_STUFFER void Stuffer::Shrink( RingState *addr, int len ) { RingState *r_p; RingState r_val; int r_len; unsigned char *w_p; unsigned char w_val; int w_space; r_p = addr; w_p = (unsigned char*)addr; w_space = 8; w_val = 0; while( len-- ) { r_val = *r_p++; r_len = ws; while (r_len>=w_space) { w_val |= ((unsigned char)r_val)&mtab[w_space]; *w_p++ = w_val; w_val = 0; r_val >>= w_space; r_len -= w_space; w_space = 8; } if (r_len) { w_val |= (((unsigned char)r_val)&mtab[w_space])<<(w_space-r_len); w_space -= r_len; } } *w_p++ = w_val; } void Stuffer::Expand( RingState *addr, int len ) { RingState *w_p; // Schreibzeiger - expandiertes Ende RingState w_val; int w_space; // fehlende Bits zum Gesamtwort unsigned char *r_p; // Lesezeiger - komprimiertes Ende unsigned char r_val; // gueltige Bits int r_len; // Zahl gueltige Bits an Lesezeigerposition r_p = (unsigned char*)addr + len*ws/8; // Adresse des letzten Byte r_len = (len*ws)%8; // gültige Bits im letzen Byte if (r_len) r_val = *r_p; // letzte Bits holen else { r_val = *--r_p; r_len = 8; } w_p = addr + len; while( len-- ) { w_space = ws; w_val = 0; while( r_len<=w_space ) { w_val <<= r_len; w_val |= r_val>>(8-r_len); w_space-= r_len; r_val = *--r_p; // ABR: liest 1 Byte ueber die Untergrenze r_len = 8; } if (w_space) { w_val <<= w_space; w_val |= r_val&mtab[w_space]; r_len -= w_space; } *--w_p = w_val; } } #else void Stuffer::Shrink( RingState *, int ) {} void Stuffer::Expand( RingState *, int ) {} #endif /*----------------------------------------------------------------------------*/ class Bitmap { public: void Clear() { memset( bits, 0, bytes_per_line*height ); } void Init( int w, int h, char *bmap ) { width = w; bytes_per_line = (width+7)/8; height = h; bits = bmap; } Bitmap( int w, int h, char *bmap ) { Init(w,h,bmap); } Bitmap() {} int GetPixel( int x, int y ) { if ((x<0)||(x>=width)||(y<0)||(y>=height)) return 0; else { unsigned char mask = 1<<(x&7); return (bits[bytes_per_line*y+(x>>3)]&mask)?1:0; } } void SetPixel( int x, int y ) { unsigned char mask = 1<<(x&7); bits[bytes_per_line*y+(x>>3)] |= mask; } void ClrPixel( int x, int y ) { unsigned char mask = 1<<(x&7); bits[bytes_per_line*y+(x>>3)] &= ~mask; } Pixmap Create() { return XCreatePixmapFromBitmapData(dpy,win,bits,width,height,1,0,1 ); } void Print(); int width; int height; protected: int bytes_per_line; char *bits; }; class LocBitmap : public Bitmap { public: void Init( int w, int h ) { Bitmap::Init(w,h,0); bits = new char[bytes_per_line*height]; Clear(); } void Init( int w, int h, FILE *fp ) { Bitmap::Init(w,h,0); bits = new char[bytes_per_line*height]; fread(bits,sizeof(char),bytes_per_line*height,fp); } void Write( FILE *fp ) { fwrite(bits,sizeof(char),bytes_per_line*height,fp); } LocBitmap( int w, int h ) { Init(w,h); } LocBitmap() {} ~LocBitmap() { delete bits; } // change for a sun sparc-station runing 4.1.3 that thinks: // xmover.c:463: no non-hidden member function 'LocBitmap::create' defined Pixmap Create() { return Bitmap::Create(); } }; void Bitmap::Print() { for(int y=0;ySetPixel(x,y); } } } bpix = bit->Create(); d_help = 2*d-1; help = XCreatePixmap(dpy,win,d_help,d_help,DefaultDepth(dpy,0)); delete bit; } void BallMover::DrawBallAt( int x, int y, int col_x ) { #ifndef NODRAW XFillRectangle(dpy,help,gc_bclear,0,0,d,d); XCopyPlane(dpy,bpix,help,gc_ball[col_x],0,0,d,d,0,0,1); Pixmap shadow = GetShadowMap(x,y); if (shadow) XCopyPlane(dpy,shadow,help,gc_lay2,0,0,d,d,0,0,1); XCopyArea(dpy,help,win,gc_bxor,0,0,d,d,x-r, y-r ); #endif } void BallMover::MoveBallOnScreen( int oldx, int oldy, int newx, int newy, int col_x ) { int dx = newx-oldx; int dy = newy-oldy; int absx = (dx>0)?dx:-dx; int absy = (dy>0)?dy:-dy; #ifndef NODRAW if ( (absx0)?0:absx; // relativer Abstand alte Position int oy = (dy>0)?0:absy; int nx = (dx>0)?absx:0; // relativer Abstand neue Position int ny = (dy>0)?absy:0; XFillRectangle(dpy,help,gc_bclear,0,0,width,height); XCopyPlane(dpy,bpix,help,gc_ball[col_x],0,0,d,d,ox,oy,1); shadow = GetShadowMap(oldx,oldy); if (shadow) XCopyPlane(dpy,shadow,help,gc_lay2,0,0,d,d,ox,oy,1); XCopyPlane(dpy,bpix,help,gc_ball[col_x],0,0,d,d,nx,ny,1); shadow = GetShadowMap(newx,newy); if (shadow) XCopyPlane(dpy,shadow,help,gc_lay2,0,0,d,d,nx,ny,1); XCopyArea(dpy,help,win,gc_bxor,0,0,width,height,oldx-ox-r, oldy-oy-r ); } else { DrawBallAt(oldx,oldy,col_x); DrawBallAt(newx,newy,col_x); } #endif #ifdef STATISTICS moves++; #endif } Pixmap BallMover::GetShadowMap( int /*x*/, int /*y*/ ) { return 0; } // ------------------------------------------------------------------------- DiscMover::DiscMover( const Real &r ) : BallMover( r ) { lpix = 0; } DiscMover::~DiscMover() { if (lpix) XFreePixmap(dpy,lpix); } void DiscMover::Init() { LocBitmap *lbit; // Kreisbitmap int x,y; double dr=r-0.5; // tatsächlicher (Welt-)Radius BallMover::Init(); lbit = new LocBitmap( d, d ); double shade_rad = 1.2; // Pixelbreite in WS-umgerechnet double inner1 = r*1/4; double inner2 = r*2/4; for (y=0;ymax_rad-shade_rad) || (dist>inner1-shade_rad)&&(distinner2-shade_rad)&&(distSetPixel(x,y); } } } } lpix = lbit->Create(); delete lbit; } Pixmap DiscMover::GetShadowMap( int /*x*/, int /*y*/ ) { return lpix; } // ------------------------------------------------------------------------- ShadedBallMover::ShadedBallMover( const Real &r ) : BallMover( r ) { lpix=0; } ShadedBallMover::~ShadedBallMover() { if (lpix) { for (int i=0;i%d bytes\n", (d+7)/8*d*lpixs_all ); for (y=0;y=0) { double f = circ_z/sqrt(dz); // Multiplikator zur Decke dx *= f; // XPos an Decke dy *= f; // YPos an Decke for (int px=0;px>1)+dx)/w2n-circ[l].x; double wy = (py*disty+(disty>>1)+dy)/w2n-circ[l].y; double d = wx*wx+wy*wy; if (d=lpixs_x) xp=lpixs_x-1; int yp = y/disty; if (yp<0) yp=0; if (yp>=lpixs_y) yp=lpixs_y-1; return lpix[xp+yp*lpixs_x]; } void ShadedBallMover::CreateLightWindow() { Window help; const int o=2; help=CreateWindow( "Beleuchtung", lpixs_x*(d+o)+1 , lpixs_y*(d+o)+1 ); for (int px=0;px1)?2.0:1.0; //sym = 1.0; if (mode) { rpixs_l = (int)(3.14*d/sym); // Zahl der Bitmaps in Längen-Richtung rpixs_b = (int)(3.14*d/2.0); // Zahl der Bitmaps in Breiten-Richtung mult = 6; // Internes Raster } else { rpixs_l = rpixs_b = 1; mult = 1; } mult2 = mult/2; rpixs_all = rpixs_l * rpixs_b; // Gesamtzahl vecs_l = rpixs_l * mult; // Hilfsraster feiner als Bitmap-Raster vecs_b = rpixs_b * mult; vecs_all = vecs_l * vecs_b; // Gesamtgröße des Rasters for (i=0;i<(int)sizeof(RingState);i++) vecs_all/=256; if (vecs_all>0) { printf( "too many RingStates (%d) -> switch type to unsigned long (in xmover.h)\n", vecs_l * vecs_b ); exit(0); } vecs_all = vecs_l * vecs_b; // Gesamtgröße des Rasters char fname[80]; sprintf(fname,"%s/fly%d-%d.dta",DATA_DIRECTORY,mode,d); FILE *fp; #ifdef DEBUG fp = (debug&ForceCalc)?0:fopen( fname, "r" ); #else fp = fopen( fname, "r" ); #endif rbit = new LocBitmap[rpixs_all]; // Anlegen der Bitmaps for (l=0;l%7d Bytes\n", (d+7)/8*d*rpixs_all ); DBG1( UnixTrace, "+ temporary: >%7d Bytes\n", d*d*(sizeof(Vec3)+sizeof(Bitmap)+(d*(d+7)/8) ) ); // // Bitmaps ausrechen // printf( "image processing: 00%%" ); fflush(stdout); for (l=0;l0.35) rbit[PixIndex(l,b)].SetPixel(x,y); } } } break; case 2: for (y=0;y1.0708 && ang<2.0708) rbit[PixIndex(l,b)].SetPixel(x,y); } } } break; case 3: for (y=0;y0.35) rbit[PixIndex(l,b)].SetPixel(x,y); } } } break; case 4: for (y=0;y0.35&&ang<1.27)||(ang>1.87&&ang<2.79)) rbit[PixIndex(l,b)].SetPixel(x,y); } } } break; default: for (y=0;y%7d Bytes (for %dx%d=%d States)\n", 4*vecs_all*sizeof(RingState), vecs_l, vecs_b, vecs_all ); right = new RingState[vecs_all]; left = new RingState[vecs_all]; up = new RingState[vecs_all]; down = new RingState[vecs_all]; // // Stuffer anlegen und initialisieren // Stuffer stuff; for (i=0,l=vecs_all;l;l>>=1) i++; // Bits in vecs_all zaehlen stuff.Init(i,sizeof(RingState)); if (fp) { printf( "reading %s\n", fname ); #ifndef LEFT_RIGHT_OPT fread( right,1,stuff.SSize(vecs_all), fp ); stuff.Expand( right, vecs_all ); fread( left, 1,stuff.SSize(vecs_all), fp ); stuff.Expand( left, vecs_all ); #endif fread( up, 1,stuff.SSize(vecs_all), fp ); stuff.Expand( up, vecs_all ); fread( down, 1,stuff.SSize(vecs_all), fp ); stuff.Expand( down, vecs_all ); } else { Real b_ang = (double)mult*M_PI/vecs_b; // Rotationswinkel pro Pixel printf( "state processing: 00%%" ); fflush(stdout); for (l=0;l XWD/rings", tw ); system(command); } #endif } #define _INFO void HalfBallMover::CreateTurnWindow() { Window win; win=CreateWindow( "Senkrecht-Rotation", rpixs_b*(d+o)+1,2*rpixs_b*(d+o)+1 ); for (int b=0;b Vec2: %03d/%03d, Pixmap: %02d/%02d\n", lv, bv, lp, bp ); if (((b^y)&1)) { XCopyPlane(dpy,bpix,win,gc_ballwhite,0,0,d,d,o/2+b*(d+o),o/2+y*(d+o),1); XCopyPlane(dpy,lpix[(b*lpixs_x/rpixs_b)+(y/2*lpixs_y/rpixs_b)*lpixs_x],win,gc_lay2,0,0,d,d,o/2+b*(d+o),o/2+y*(d+o),1); XCopyPlane(dpy,rpix[PixIndex(nst)],win,gc_ball[7],0,0,d,d,o/2+b*(d+o),o/2+y*(d+o),1); } else #endif { XCopyPlane(dpy,bpix,win,gc_ballwhite,0,0,d,d,o/2+b*(d+o),o/2+y*(d+o),1); XCopyPlane(dpy,lpix[(b*lpixs_x/rpixs_b)+(y/2*lpixs_y/rpixs_b)*lpixs_x],win,gc_lay2,0,0,d,d,o/2+b*(d+o),o/2+y*(d+o),1); XCopyPlane(dpy,rpix[PixIndex(st)],win,gc_ball[8],0,0,d,d,o/2+b*(d+o),o/2+y*(d+o),1); } st = Turn(st,0,1); } } #ifdef DEBUG if (debug&xwd) { char command[200]; XSync(dpy,0); sprintf( command, "/usr/bin/X11/xwd -id %ld -frame > XWD/turns", win ); system(command); } #endif } #if (1) #define LINE(DY,YDEC,YADDOP,YTAB,DX,XLESS,XTAB) \ int dy_c = DY; \ int c = 0; \ while(DY) { \ st = YTAB [st]; \ DY YDEC; \ c -= DX; \ if (c XLESS 0) { \ c YADDOP dy_c; \ st = XTAB [st]; \ }; \ } RingState HalfBallMover::Turn( RingState st, int dx, int dy ) { if (dx>0) { if (dy>0) { if (dy>dx) { LINE(dy,--,+=,up, dx,<,right); } else { LINE(dx,--,+=,right,dy,<,up); } } else { // dy negativ => alle DY-Parameter umkehren if (-dy>dx) { LINE(dy,++,-=,down, dx,<,right); } else { LINE(dx,--,+=,right,dy,>,down); } } } else { // dx negativ => alle DX-Parameter umkehren if (dy>0) { if (dy>-dx) { LINE(dy,--,+=,up, dx,>,left); } else { LINE(dx,++,-=,left ,dy,<,up); } } else { // dy negativ => alle DY-Parameter umkehren if (-dy>-dx){ LINE(dy,++,-=,down, dx,>,left); } else { LINE(dx,++,-=,left ,dy,>,down); } } } return st; } #else RingState HalfBallMover::Turn( RingState st, int dx, int dy ) { while( dx && dy ) { if (dx>0) { dx--; st = right[st]; } else if (dx<0) { dx++; st = left[st]; } if (dy>0) { dy--; st = up[st]; } else if (dy<0) { dy++; st = down[st]; } } if (dx>0) { while(dx--) st = right[st]; } else if (dx<0) { while(dx++) st = left[st]; } if (dy>0) { while(dy--) st = up[st]; } else if (dy<0) { while(dy++) st = down[st]; } return st; } #endif #ifdef DEBUG void HalfBallMover::ShowDebugRing(RingState st, int col_x) { int l,b; St2AngPix(st,&l,&b); XCopyPlane(dpy,nbpix,tw,gc_ballwhite, 0,0,d+o,d+o,(rpixs_l-1-l)*(d+o),b*(d+o),1); XCopyPlane(dpy,nbpix,tw,gc_ball[col_x],0,0,d+o,d+o,(rpixs_l-1-l)*(d+o),b*(d+o),1); } #endif void HalfBallMover::RollBallAt( int x, int y, RingState st, int col_x ) { #ifndef NODRAW XFillRectangle(dpy,help,gc_bclear,0,0,d,d); XCopyPlane(dpy,bpix,help,gc_ballwhite,0,0,d,d,0,0,1); XCopyPlane(dpy,GetShadowMap(x,y),help,gc_lay2,0,0,d,d,0,0,1); XCopyPlane(dpy,rpix[PixIndex(st)],help,gc_ball[col_x],0,0,d,d,0,0,1); XCopyArea(dpy,help,win,gc_bxor,0,0,d,d,x-r, y-r ); #ifdef DEBUG if ((tw)&&(debug&ShowRings)) ShowDebugRing(st,col_x); #endif #endif } void HalfBallMover::RollBallOnScreen( int oldx, int oldy, RingState ost, int newx, int newy, RingState *nst, int col_x ) { int dx = newx-oldx; int dy = newy-oldy; *nst = Turn( ost, dx, dy ); int absx = (dx>0)?dx:-dx; int absy = (dy>0)?dy:-dy; #ifndef NODRAW if ( (absx0)?0:absx; // relativer Abstand alte Position int oy = (dy>0)?0:absy; int nx = (dx>0)?absx:0; // relativer Abstand neue Position int ny = (dy>0)?absy:0; XFillRectangle(dpy,help,gc_bclear,0,0,width,height); XCopyPlane(dpy,bpix,help,gc_ballwhite,0,0,d,d,ox,oy,1); XCopyPlane(dpy,GetShadowMap(oldx,oldy),help,gc_lay2,0,0,d,d,ox,oy,1); XCopyPlane(dpy,rpix[PixIndex(ost)],help,gc_ball[col_x],0,0,d,d,ox,oy,1); XCopyPlane(dpy,bpix,help,gc_ballwhite,0,0,d,d,nx,ny,1); XCopyPlane(dpy,GetShadowMap(newx,newy),help,gc_lay2,0,0,d,d,nx,ny,1); XCopyPlane(dpy,rpix[PixIndex(*nst)],help,gc_ball[col_x],0,0,d,d,nx,ny,1); XCopyArea(dpy,help,win,gc_bxor,0,0,width,height,oldx-ox-r, oldy-oy-r ); #ifdef DEBUG if ((tw)&&(debug&ShowRings)) { ShowDebugRing(ost,col_x); ShowDebugRing(*nst,col_x); } #endif } else { RollBallAt(oldx,oldy,ost,col_x); RollBallAt(newx,newy,*nst,col_x); } #endif #ifdef STATISTICS moves++; #endif } // --------------------------------------------------------------------------- // LoEnhancedHalfBallMover // --------------------------------------------------------------------------- LoEnhancedHalfBallMover::LoEnhancedHalfBallMover( const Real &r, int mode_in ) : HalfBallMover( r, mode_in ) { lbpix = 0; boffset = 0; lb_dist = boffset+d; } LoEnhancedHalfBallMover::~LoEnhancedHalfBallMover() { if (lbpix) { PixmapCache::pcache.Unlock( dpy, lbpix ); lbpix = 0; } } void LoEnhancedHalfBallMover::Init() { HalfBallMover::Init(); // create initialisation pixmap for all possible shades, which // can directly be used as presets in the RollBall() functions // A large Pixmap is created that contains all neccessary background-presets // in a certain distance, so that a direct copy can be done, which presets // the whole background help-pixmap. lbpix = PixmapCache::pcache.Lock( d, ENHANCED_LBPIX_ID ); if (!lbpix ) { // create map and clear it DBG1( UnixTrace, "EnhancedData: >%d bytes\n", (lb_dist*lpixs_x+d-1)*(lb_dist*lpixs_y+d-1) ); lbpix = XCreatePixmap(dpy,win, lb_dist*lpixs_x+d-1,lb_dist*lpixs_y+d-1,DefaultDepth(dpy,0)); XFillRectangle(dpy,lbpix,gc_bclear, 0,0, lb_dist*lpixs_x+d-1,lb_dist*lpixs_y+d-1); // copy the balls & shades into the map for (int x=0;x0)?dx:-dx; int absy = (dy>0)?dy:-dy; #ifndef NODRAW if ( (absx0)?0:absx; // relativer Abstand alte Position int oy = (dy>0)?0:absy; int nx = (dx>0)?absx:0; // relativer Abstand neue Position int ny = (dy>0)?absy:0; int xp,yp; WC2Index(oldx,oldy,&xp,&yp); XFillRectangle(dpy,help,gc_bclear,0,0,width,height); XCopyArea(dpy,lbpix,help,gc_bxor, boffset-ox+xp*lb_dist,boffset-oy+yp*lb_dist,d_help,d_help,0,0); XCopyPlane(dpy,rpix[PixIndex(ost)],help,gc_ball[col_x],0,0,d,d,ox,oy,1); WC2Index(newx,newy,&xp,&yp); XCopyArea(dpy,lbpix,help,gc_bxor, boffset+xp*lb_dist,boffset+yp*lb_dist,d,d,nx,ny); XCopyPlane(dpy,rpix[PixIndex(*nst)],help,gc_ball[col_x],0,0,d,d,nx,ny,1); XCopyArea(dpy,help,win,gc_bxor,0,0,width,height,oldx-ox-r, oldy-oy-r ); #ifdef DEBUG if ((tw)&&(debug&ShowRings)) { ShowDebugRing(ost,col_x); ShowDebugRing(*nst,col_x); } #endif } else { RollBallAt(oldx,oldy,ost,col_x); RollBallAt(newx,newy,*nst,col_x); } #endif #ifdef STATISTICS moves++; #endif } // --------------------------------------------------------------------------- // HiEnhancedHalfBallMover // --------------------------------------------------------------------------- HiEnhancedHalfBallMover::HiEnhancedHalfBallMover( const Real &r, int mode_in ) : LoEnhancedHalfBallMover( r, mode_in ) { boffset = d-1; lb_dist = 2*d-1; } HiEnhancedHalfBallMover::~HiEnhancedHalfBallMover() { } void HiEnhancedHalfBallMover::RollBallOnScreen( int oldx, int oldy, RingState ost, int newx, int newy, RingState *nst, int col_x ) { #ifdef DEBUG if (enhanced_mover<2) { LoEnhancedHalfBallMover::RollBallOnScreen(oldx,oldy,ost,newx,newy,nst,col_x); return; } #endif int dx = newx-oldx; int dy = newy-oldy; *nst = Turn( ost, dx, dy ); int absx = (dx>0)?dx:-dx; int absy = (dy>0)?dy:-dy; #ifndef NODRAW if ( (absx0)?0:absx; // relativer Abstand alte Position int oy = (dy>0)?0:absy; int nx = (dx>0)?absx:0; // relativer Abstand neue Position int ny = (dy>0)?absy:0; int xp,yp; WC2Index(oldx,oldy,&xp,&yp); XCopyArea(dpy,lbpix,help,gc_default, boffset-ox+xp*lb_dist,boffset-oy+yp*lb_dist,d_help,d_help,0,0); XCopyPlane(dpy,rpix[PixIndex(ost)],help,gc_ball[col_x],0,0,d,d,ox,oy,1); WC2Index(newx,newy,&xp,&yp); XCopyArea(dpy,lbpix,help,gc_bxor, boffset+xp*lb_dist,boffset+yp*lb_dist,d,d,nx,ny); XCopyPlane(dpy,rpix[PixIndex(*nst)],help,gc_ball[col_x],0,0,d,d,nx,ny,1); XCopyArea(dpy,help,win,gc_bxor,0,0,width,height,oldx-ox-r, oldy-oy-r ); #ifdef DEBUG if ((tw)&&(debug&ShowRings)) { ShowDebugRing(ost,col_x); ShowDebugRing(*nst,col_x); } #endif } else { RollBallAt(oldx,oldy,ost,col_x); RollBallAt(newx,newy,*nst,col_x); } #endif #ifdef STATISTICS moves++; #endif }