diff options
| -rw-r--r-- | main.cpp | 22 | ||||
| -rw-r--r-- | screenbuffer.cpp | 152 | ||||
| -rw-r--r-- | screenbuffer.h | 24 | ||||
| -rw-r--r-- | world.cpp | 24 | ||||
| -rw-r--r-- | world.h | 4 | 
5 files changed, 216 insertions, 10 deletions
@@ -8,6 +8,7 @@  #include <tuple>  #include <cctype>  #include <cassert> +#include <signal.h>  #include <sys/time.h>  #include "params.h"  #include "world.h" @@ -210,6 +211,16 @@ string preprocess(istream &file){  } +static ScreenBuffer *sb; + +static void signalHandler(int sig){ +	if(sig==SIGINT){ +		sb->emergencyDeinit(); +		_exit(130); +	} +} + +  int main(int argc,char **argv){  	struct timeval tv;  	gettimeofday(&tv,nullptr); @@ -249,11 +260,14 @@ int main(int argc,char **argv){  		}  	} -	world.print(); +	sb=new ScreenBuffer(SIZE*3,SIZE); +	signal(SIGINT,signalHandler); +	world.print(*sb);  	for(int i=0;i<C::autoTimeout;i++){ -		world.tick();  		usleep(3000); -		cout<<"\x1B[2J\x1B[H"; -		world.print(); +		world.tick(); +		world.print(*sb); +		sb->draw();  	} +	delete sb;  } diff --git a/screenbuffer.cpp b/screenbuffer.cpp new file mode 100644 index 0000000..a382e82 --- /dev/null +++ b/screenbuffer.cpp @@ -0,0 +1,152 @@ +#define _GNU_SOURCE +#include <iostream> +#include <cstdarg> +#include <cstdlib> +#include <cstring> +#include <cassert> +#include <unistd.h> +#include "screenbuffer.h" + +using namespace std; + + +static void initTerminal(){ +	cout<<"\x1B[?1049h"  // start alternate screen +	      "\x1B[2J"  // clear screen +	      "\x1B[H"  // move to top left +	    <<flush; +} + +static void deinitTerminal(){ +	cout<<"\x1B[?1049l"  // end alternate screen +	    <<flush; +} + + +ScreenBuffer::ScreenBuffer(int W,int H) +		:W(W),H(H){ +	screen=new char[W*H]; +	prevscreen=new char[W*H]; +	memset(screen,' ',W*H); +	memset(prevscreen,' ',W*H); +	initTerminal(); +} + +ScreenBuffer::~ScreenBuffer(){ +	delete[] screen; +	delete[] prevscreen; +	deinitTerminal(); +} + +void ScreenBuffer::moveto(int x,int y){ +	assert(x>=0&&x<W&&y>=0&&y<H); +	curx=x; +	cury=y; +} + +void ScreenBuffer::printstr(const char *buf){ +	int basex=curx; +	for(int i=0;buf[i];i++){ +		if(buf[i]!='\n'){ +			if(curx==W){ +				if(cury==H-1){ +					curx--; +					break; +				} +				curx=basex; +				cury++; +			} +			screen[W*cury+curx]=buf[i]; +			curx++; +		} else { +			if(cury==H-1)break; +			curx=basex; +		} +	} + +	if(curx==W){ +		if(cury==H-1){ +			curx--; +		} else { +			curx=basex; +			cury++; +		} +	} +} + +__attribute__((format (printf, 2, 3))) +int ScreenBuffer::printf(const char *format,...){ +	va_list ap; +	va_start(ap,format); +	char *buf; +	int ret=vasprintf(&buf,format,ap); +	va_end(ap); +	assert(buf); + +	printstr(buf); + +	free(buf); +	return ret; +} + +__attribute__((format (printf, 4, 5))) +int ScreenBuffer::mvprintf(int x,int y,const char *format,...){ +	moveto(x,y); + +	va_list ap; +	va_start(ap,format); +	char *buf; +	int ret=vasprintf(&buf,format,ap); +	va_end(ap); +	assert(buf); + +	printstr(buf); + +	free(buf); +	return ret; +} + +void ScreenBuffer::draw(){ +	bool changed[W*H]; +	for(int i=0;i<W*H;i++){ +		changed[i]=screen[i]!=prevscreen[i]; +	} +	for(int y=0;y<H;y++){ +		for(int x=1;x<W-1;x++){ +			changed[W*y+x]|=changed[W*y+x-1]&changed[W*y+x+1]; +		} +	} + +	bool prepped=false; + +	for(int y=0;y<H;y++){ +		bool needtomove=true; +		for(int x=0;x<W;x++){ +			if(!changed[W*y+x]){ +				needtomove=true; +				continue; +			} +			if(!prepped){ +				cout<<"\x1B[?25l";  // invisible cursor +				prepped=true; +			} +			if(needtomove){ +				cout<<"\x1B["<<y+1<<';'<<x+1<<'H'; +				needtomove=false; +			} +			cout.put(screen[W*y+x]); +		} +	} +	if(prepped){ +		cout<<"\x1B[?12;25h"  // visible cursor +		    <<"\x1B["<<H<<';'<<W<<'H'  // move past screen +		    <<flush; +	} + +	memcpy(prevscreen,screen,W*H); +} + +void ScreenBuffer::emergencyDeinit(){ +	static const char *str="\n\x1B[?1049l\x1B[12;25h^C\n"; +	write(1,str,strlen(str)); +} diff --git a/screenbuffer.h b/screenbuffer.h new file mode 100644 index 0000000..49cc5c5 --- /dev/null +++ b/screenbuffer.h @@ -0,0 +1,24 @@ +#pragma once + +using namespace std; + + +class ScreenBuffer{ +	int W,H; +	char *prevscreen,*screen; +	int curx=0,cury=0; + +	void printstr(const char *buf); + +public: +	ScreenBuffer(int W,int H); +	~ScreenBuffer(); + +	void moveto(int x,int y); +	int printf(const char *format,...) __attribute__((format (printf, 2, 3))); +	int mvprintf(int x,int y,const char *format,...) __attribute__((format (printf, 4, 5))); + +	void draw(); + +	void emergencyDeinit(); +}; @@ -316,14 +316,28 @@ Robot* World::targetbot(const Robot *r){  	return *targetbotptr(r);  } -void World::print() const { +void World::print(ostream &os) const {  	for(int y=0;y<SIZE;y++){  		for(int x=0;x<SIZE;x++){ -			if(board[y][x]==nullptr)cout<<".."; -			else cout<<string(2,"^>v<"[board[y][x]->heading%4]); -			cout<<' '; +			if(board[y][x]==nullptr)os<<".."; +			else os<<string(2,"^>v<"[board[y][x]->heading%4]); +			os<<' '; +		} +		os<<endl; +	} +} + +void World::print(ScreenBuffer &sb) const { +	for(int y=0;y<SIZE;y++){ +		sb.moveto(0,y); +		for(int x=0;x<SIZE;x++){ +			if(board[y][x]==nullptr)sb.printf(".."); +			else { +				char c="^>v<"[board[y][x]->heading%4]; +				sb.printf("%c%c",c,c); +			} +			sb.printf(" ");  		} -		cout<<endl;  	}  } @@ -6,6 +6,7 @@  #include <vector>  #include <cstdint>  #include "params.h" +#include "screenbuffer.h"  using namespace std; @@ -100,7 +101,8 @@ public:  	void tick();  	Robot* targetbot(const Robot *r); -	void print() const; +	void print(ostream &os) const; +	void print(ScreenBuffer &sb) const;  };  | 
