Schnellere Alternative zu printf, write und setchar



  • @lemon03 sagte in Schnellere Alternative zu printf, write und setchar:

    Der Flaschenhals wird immer der output in die Konsole sein.

    Mhm. Und einen neuen Prozess starten um die Ausgabe zu "leeren" ist noch lahmer.

    @lemon03 sagte in Schnellere Alternative zu printf, write und setchar:

    Ups, mich auch.

    Und Du hast Dich im Forum geirrt. Hier ist C, nicht C++. 😛



  • @Swordfish sagte in Schnellere Alternative zu printf, write und setchar:

    @lemon03 sagte in Schnellere Alternative zu printf, write und setchar:

    Der Flaschenhals wird immer der output in die Konsole sein.

    Mhm. Und einen neuen Prozess starten um die Ausgabe zu "leeren" ist noch lahmer.

    @lemon03 sagte in Schnellere Alternative zu printf, write und setchar:

    Ups, mich auch.

    Und Du hast Dich im Forum geirrt. Hier ist C, nicht C++. 😛

    Und? Die WinAPI ist eine C API und daher Windows = C++ anzunehmen ist da doch etwas weit hergeholt



  • @firefly sagte in Schnellere Alternative zu printf, write und setchar:

    Und? Die WinAPI ist eine C API und daher Windows = C++ anzunehmen ist da doch etwas weit hergeholt

    @firefly sagte in Schnellere Alternative zu printf, write und setchar:

    Hier ist C, nicht C++.

    bezog sich auf

    @lemon03 sagte in Schnellere Alternative zu printf, write und setchar:

    void Console::writeBuffer()
    {
        const std::vector<CHAR_INFO> outbuffer = convertToCHAR_INFO();
        // ...
    

    Vielleicht erstmal Kaffee, fly?



  • Vielen Dank für all die schnellen Antworten, ich werde sie mir mal genauer ansehen. Warum soll ich keine Globalen Variablen benutzen? Ich habe ursprünglich nur auf lokale gesetzt, allerdings hat das nur zu mehr Code und weniger Übersichtlichkeit geführt.

    Danke!

    Daniel



  • @daniel Das einzige vernünftige an deinem Code ist die Formatierung/Einrückung.

    Der Rest ist Schrott.

    Wenn du in C programmieren willst, dann programmiere C und nicht Python.

    Python ist schließlich nicht Fortran.





  • Geschmacksmuster mit ncurses (Windows: pdcurses):

    #include <stdlib.h>  // EXIT_FAILURE
    #include <stdio.h>   // fputs()
    
    #ifdef __linux__
    #	include <unistd.h>
    #	define delay(micro_seconds) usleep((micro_seconds))
    #elif _WIN32
    #	define _WIN32_LEAN_AND_MEAN
    #	include <windows.h>
    #	undef MOUSE_MOVED // conflicts with curses.h
    #	define delay(micro_seconds) Sleep((micro_seconds))
    #endif
    
    #include "curses.h"   // initscr(), resize_term(), refresh()
    
    
    typedef enum error_tag {
    	ERROR_NONE, ERROR_WINDOW_RESIZE, ERROR_NOECHO, ERROR_SET_CURSOR, ERROR_SET_NODELAY,
    	ERROR_SET_CBREAK, ERROR_DRAW_BORDER, ERROR_WINDOW_REFRESH, ERROR_PLAYER_WINDOW,
    } error_t;
    
    char const * const error_message[] = {
    	"No error.",
    	"Couldn't resize the console window.",
    	"Couldn't set no echo mode."
    	"Couldn't set cursor size.",
    	"Couldn't set no delay mode.",
    	"Couldn't set cbreak-mode.",
    	"Couldn't draw border.",
    	"Couldn't refresh window.",
    	"Couldn't create player window.",
    };
    
    void handle_error(error_t error)
    {
    	endwin();
    	fputs(error_message[error], stderr);
    	fputs("\n\n", stderr);
    }
    
    
    enum { PLAYFIELD_WIDTH = 168, PLAYFIELD_HEIGHT = 43, DELAY = 100, PLAYER_GLYPH = 'X' };
    enum { KEY_MOVE_UP = 'w', KEY_MOVE_DOWN = 's', KEY_MOVE_RIGHT = 'd', KEY_MOVE_LEFT = 'a', KEY_QUIT = 0x1b };  // 0x1b == ESC
    
    
    typedef struct position_tag {
    	int x, y;
    } position_t;
    
    typedef enum direction_tag {
    	DIRECTION_NONE, DIRECTION_UP, DIRECTION_DOWN, DIRECTION_LEFT, DIRECTION_RIGHT
    } direction_t;
    
    typedef struct player_tag {
    	position_t   position;
    	direction_t  direction;
    	WINDOW      *window;
    } player_t;
    
    error_t player_create(player_t *player)
    {
    	player_t new_player = { { PLAYFIELD_WIDTH / 2, PLAYFIELD_HEIGHT / 2 }, DIRECTION_NONE };
    
    	new_player.window = newwin(1, 1, new_player.position.y, new_player.position.x);
    	if (!new_player.window)
    		return ERROR_PLAYER_WINDOW;
    
    	waddch(new_player.window, PLAYER_GLYPH);
    
    	*player = new_player;
    	return ERROR_NONE;
    }
    
    bool player_move(player_t *player)
    {
    	switch (player->direction) {
    	case DIRECTION_UP:
    		if (--player->position.y == 0)
    			return FALSE;
    		break;
    	case DIRECTION_DOWN:
    		if (++player->position.y == PLAYFIELD_HEIGHT - 1)
    			return FALSE;
    		break;
    	case DIRECTION_LEFT:
    		if (--player->position.x == 0)
    			return FALSE;
    		break;
    	case DIRECTION_RIGHT:
    		if (++player->position.x == PLAYFIELD_WIDTH - 1)
    			return FALSE;
    		break;
    	}
    
    	wclear(player->window);
    	wrefresh(player->window);
    	mvwin(player->window, player->position.y, player->position.x);
    	waddch(player->window, PLAYER_GLYPH);
    	wrefresh(player->window);
    
    	return TRUE;
    }
    
    void player_destroy(player_t *player)
    {
    	delwin(player->window);
    }
    
    
    error_t draw_border(WINDOW *window)
    {
    	if (box(window, 0, 0) == ERR)
    		return ERROR_DRAW_BORDER;
    
    	if (refresh() == ERR)
    		return ERROR_WINDOW_REFRESH;
    
    	return ERROR_NONE;
    }
    
    error_t init_ncurses(WINDOW *window)
    {
    	if (resize_term(PLAYFIELD_HEIGHT, PLAYFIELD_WIDTH) == ERR)
    		return ERROR_WINDOW_RESIZE;
    
    	if (noecho() == ERR)
    		return ERROR_NOECHO;
    
    	if (cbreak() == ERR)
    		return ERROR_SET_CBREAK;
    
    	if (curs_set(0) == ERR)
    		return ERROR_SET_CURSOR;
    
    	if (nodelay(window, TRUE) == ERR)
    		return ERROR_SET_NODELAY;
    
    	return ERROR_NONE;
    }
    
    bool handle_input(WINDOW *window, player_t *player)
    {
    	int ch;
    	if ((ch = getch()) == ERR)
    		return FALSE;
    
    	BOOL result = FALSE;
    
    	switch(ch) {
    	case KEY_MOVE_UP:
    		player->direction = DIRECTION_UP;
    		break;
    	case KEY_MOVE_DOWN:
    		player->direction = DIRECTION_DOWN;
    		break;
    	case KEY_MOVE_RIGHT:
    		player->direction = DIRECTION_RIGHT;
    		break;
    	case KEY_MOVE_LEFT:
    		player->direction = DIRECTION_LEFT;
    		break;
    	case KEY_QUIT:
    		result = TRUE;
    	}
    
    	while (getch() != ERR);
    
    	return result;
    }
    
    
    int main(void)
    {
    	WINDOW *window = initscr();
    	error_t error = init_ncurses(window);
    
    	if (error != ERROR_NONE) {
    		handle_error(error);
    		return EXIT_FAILURE;
    	}
    
    	error = draw_border(window);
    	if (error != ERROR_NONE) {
    		handle_error(error);
    		return EXIT_FAILURE;
    	}
    
    	player_t player;
    	error = player_create(&player);
    	if (error != ERROR_NONE) {
    		handle_error(error);
    		return EXIT_FAILURE;
    	}
    
    	bool alive = TRUE;
    	bool quit = FALSE;
    	do {
    		quit = handle_input(window, &player);
    		alive = player_move(&player);
    		delay(DELAY);
    	} while (alive && !quit);
    
    	player_destroy(&player);
    	endwin();
    }
    

    Noch 'nen Schwanz wachsen lassen und Snake ist (halb-) fertig.



  • Aufgeblasen zu Snake:

    #include <stddef.h>  // size_t
    #include <stdlib.h>  // EXIT_FAILURE, srand(), rand(), malloc(), realloc(), free()
    #include <stdio.h>   // fputs()
    #include <time.h>    // time()
    
    #ifdef __linux__
    #	include <unistd.h>
    #	define delay(micro_seconds) usleep((micro_seconds))
    #elif _WIN32
    #	define _WIN32_LEAN_AND_MEAN
    #	include <windows.h>
    #	undef MOUSE_MOVED // conflicts with curses.h
    #	define delay(micro_seconds) Sleep((micro_seconds))
    #endif
    
    #include "curses.h"   // WINDOW, initscr(), resize_term(), refresh(), wrefresh(), newwin(), delwin(), mvwin(),
                          // waddch(), wclear(), endwin(), box(), getch(), ...
    
    
    typedef enum error_tag {
    	ERROR_NONE, ERROR_WINDOW_RESIZE, ERROR_NOECHO, ERROR_SET_CURSOR, ERROR_SET_NODELAY,
    	ERROR_SET_CBREAK, ERROR_DRAW_BORDER, ERROR_WINDOW_REFRESH, ERROR_PLAYER_WINDOW,
    	ERROR_FRUIT_WINDOW, ERROR_MEMORY,
    } error_t;
    
    char const *error_message[] = {
    	"No error.",
    	"Couldn't resize the console window.",
    	"Couldn't set no echo mode."
    	"Couldn't set cursor size.",
    	"Couldn't set no delay mode.",
    	"Couldn't set cbreak-mode.",
    	"Couldn't draw border.",
    	"Couldn't refresh window.",
    	"Couldn't create player window.",
    	"Couldn't create fruit window.",
    	"Out of memory."
    };
    
    void handle_error(error_t error)
    {
    	endwin();
    	fputs(error_message[error], stderr);
    	fputs("\n\n", stderr);
    }
    
    
    enum { PLAYFIELD_WIDTH = 168, PLAYFIELD_HEIGHT = 43, HDELAY = 50, VDELAY = 100, PLAYER_GLYPH = 'X', FRUIT_GLYPH = 'O' };
    enum { KEY_MOVE_UP = 'w', KEY_MOVE_DOWN = 's', KEY_MOVE_RIGHT = 'd', KEY_MOVE_LEFT = 'a', KEY_QUIT = 0x1b };
    
    
    typedef struct position_tag {
    	int x, y;
    } position_t;
    
    typedef enum direction_tag {
    	DIRECTION_NONE, DIRECTION_UP, DIRECTION_DOWN, DIRECTION_LEFT, DIRECTION_RIGHT
    } direction_t;
    
    
    typedef struct player_tag {
    	position_t   *position;
    	direction_t   old_direction;
    	direction_t   direction;
    	WINDOW       *head;
    	WINDOW       *tail;
    	size_t        size;
    	size_t        length;
    	size_t        growing;
    	bool          alive;
    } player_t;
    
    error_t player_create(player_t *player)
    {
    	player_t new_player = { 0 };
    
    	new_player.position = malloc(sizeof(*new_player.position));
    	if (!new_player.position)
    		return ERROR_MEMORY;
    
    	new_player.position[0].x = PLAYFIELD_WIDTH / 2;
    	new_player.position[0].y = PLAYFIELD_HEIGHT / 2;
    
    	new_player.head = newwin(1, 1, new_player.position[0].y, new_player.position[0].x);
    	if (!new_player.head) {
    		free(new_player.position);
    		return ERROR_PLAYER_WINDOW;
    	}
    
    	new_player.tail = newwin(1, 1, new_player.position[0].y, new_player.position[0].x);
    	if (!new_player.tail) {
    		free(new_player.position);
    		delwin(player->head);
    		return ERROR_PLAYER_WINDOW;
    	}
    
    	waddch(new_player.head, PLAYER_GLYPH);
    
    	new_player.size = 1;
    	new_player.length = 1;
    	new_player.growing = 0;
    	new_player.alive = TRUE;
    
    	*player = new_player;
    	return ERROR_NONE;
    }
    
    bool player_is_within_body(player_t const *player, position_t const *position)
    {
    	for (size_t i = 1; i < player->length; ++i)
    		if (position->x == player->position[i].x && position->y == player->position[i].y)
    			return TRUE;
    	return FALSE;
    }
    
    bool player_is_cannibal(player_t const *player)
    {
    	return
    		player->length > 1 &&
    		( player->direction == DIRECTION_UP && player->old_direction == DIRECTION_DOWN ||
    		  player->direction == DIRECTION_DOWN && player->old_direction == DIRECTION_UP ||
    		  player->direction == DIRECTION_LEFT && player->old_direction == DIRECTION_RIGHT ||
    		  player->direction == DIRECTION_RIGHT && player->old_direction == DIRECTION_LEFT
    		) || player_is_within_body(player, &player->position[0]);
    }
    
    bool player_is_alive(player_t const *player)
    {
    	return player->alive;
    }
    
    error_t player_move(player_t *player)
    {
    	position_t new_head = player->position[0];
    
    	if (player_is_cannibal(player)) {
    		player->alive = FALSE;
    	}
    	else {
    		switch (player->direction) {
    		case DIRECTION_UP:
    			if (--new_head.y == 0)
    				player->alive = FALSE;
    			break;
    		case DIRECTION_DOWN:
    			if (++new_head.y == PLAYFIELD_HEIGHT - 1)
    				player->alive = FALSE;
    			break;
    		case DIRECTION_LEFT:
    			if (--new_head.x == 0)
    				player->alive = FALSE;
    			break;
    		case DIRECTION_RIGHT:
    			if (++new_head.x == PLAYFIELD_WIDTH - 1)
    				player->alive = FALSE;
    			break;
    		}
    	}
    
    	if (!player->alive)
    		return ERROR_NONE;
    
    	if (player->growing && player->length == player->size) {
    		position_t *tmp = realloc(player->position, (player->size + 5) * sizeof(*tmp));
    		if (!tmp)
    			return ERROR_MEMORY;
    		player->position = tmp;
    		player->size += 5;
    	}
    
    
    	for (size_t i = player->length - !(player->growing); i; --i)
    		player->position[i] = player->position[i - 1];
    	player->position[0] = new_head;
    
    	if (!player->growing) {
    		wclear(player->tail);
    		wrefresh(player->tail);
    		mvwin(player->tail, player->position[player->length - 1].y, player->position[player->length - 1].x);
    	}
    	else {
    		--player->growing;
    		++player->length;
    	}
    
    	mvwin(player->head, player->position[0].y, player->position[0].x);
    	waddch(player->head, PLAYER_GLYPH);
    	wrefresh(player->head);
    
    	return ERROR_NONE;
    }
    
    bool player_is_moving_horizontally(player_t const *player)
    {
    	return player->direction == DIRECTION_RIGHT || player->direction == DIRECTION_LEFT;
    }
    
    void player_destroy(player_t *player)
    {
    	delwin(player->head);
    	delwin(player->tail);
    	free(player->position);
    }
    
    
    typedef struct fruit_tag {
    	position_t  position;
    	WINDOW     *window;
    } fruit_t;
    
    error_t fruit_create(fruit_t *fruit, player_t const *player)
    {
    	fruit_t new_fruit;
    
    	do {
    		new_fruit.position.x = rand() % (PLAYFIELD_WIDTH - 2) + 1;
    		new_fruit.position.y = rand() % (PLAYFIELD_HEIGHT - 2) + 1;
    	} while (new_fruit.position.x == player->position[0].x &&
    	         new_fruit.position.y == player->position[0].y ||
    	         player_is_within_body(player, &new_fruit.position));
    
    
    	new_fruit.window = newwin(1, 1, new_fruit.position.y, new_fruit.position.x);
    	if (!new_fruit.window)
    		return ERROR_FRUIT_WINDOW;
    
    	waddch(new_fruit.window, FRUIT_GLYPH);
    
    	*fruit = new_fruit;
    	return ERROR_NONE;
    }
    
    error_t fruit_update(fruit_t *fruit)
    {
    	wrefresh(fruit->window);
    	return ERROR_NONE;
    }
    
    void fruit_destroy(fruit_t *fruit)
    {
    	delwin(fruit->window);
    }
    
    
    bool player_eat(player_t *player, fruit_t *fruit)
    {
    	if (player->position[0].x == fruit->position.x &&
    	    player->position[0].y == fruit->position.y)
    	{
    		fruit_destroy(fruit);
    		fruit_create(fruit, player);
    		fruit_update(fruit);
    		return TRUE;
    	}
    
    	return FALSE;
    }
    
    
    error_t draw_border(WINDOW *window)
    {
    	if (box(window, 0, 0) == ERR)
    		return ERROR_DRAW_BORDER;
    
    	if (refresh() == ERR)
    		return ERROR_WINDOW_REFRESH;
    
    	return ERROR_NONE;
    }
    
    error_t init_ncurses(WINDOW *window)
    {
    	if (resize_term(PLAYFIELD_HEIGHT, PLAYFIELD_WIDTH) == ERR)
    		return ERROR_WINDOW_RESIZE;
    
    	if (noecho() == ERR)
    		return ERROR_NOECHO;
    
    	if (cbreak() == ERR)
    		return ERROR_SET_CBREAK;
    
    	if (curs_set(0) == ERR)
    		return ERROR_SET_CURSOR;
    
    	if (nodelay(window, TRUE) == ERR)
    		return ERROR_SET_NODELAY;
    
    	return ERROR_NONE;
    }
    
    bool handle_input(WINDOW *window, player_t *player)
    {
    	int ch;
    	if ((ch = getch()) == ERR)
    		return FALSE;
    
    	bool result = FALSE;
    
    	switch (ch) {
    	case KEY_MOVE_UP:
    		player->old_direction = player->direction;
    		player->direction = DIRECTION_UP;
    		break;
    	case KEY_MOVE_DOWN:
    		player->old_direction = player->direction;
    		player->direction = DIRECTION_DOWN;
    		break;
    	case KEY_MOVE_RIGHT:
    		player->old_direction = player->direction;
    		player->direction = DIRECTION_RIGHT;
    		break;
    	case KEY_MOVE_LEFT:
    		player->old_direction = player->direction;
    		player->direction = DIRECTION_LEFT;
    		break;
    	case KEY_QUIT:
    		result = TRUE;
    	}
    
    	while (getch() != ERR);
    
    	return result;
    }
    
    
    int main(void)
    {
    	WINDOW *window = initscr();
    	error_t error = init_ncurses(window);
    
    	if (error != ERROR_NONE) {
    		handle_error(error);
    		return EXIT_FAILURE;
    	}
    
    	error = draw_border(window);
    	if (error != ERROR_NONE) {
    		handle_error(error);
    		return EXIT_FAILURE;
    	}
    
    	player_t player;
    	error = player_create(&player);
    	if (error != ERROR_NONE) {
    		handle_error(error);
    		return EXIT_FAILURE;
    	}
    
    	srand((unsigned)time(NULL));
    	fruit_t fruit;
    	error = fruit_create(&fruit, &player);
    	if (error != ERROR_NONE) {
    		handle_error(error);
    		return EXIT_FAILURE;
    	}
    
    	fruit_update(&fruit);
    
    	bool quit = FALSE;
    	do {
    		quit = handle_input(window, &player);
    		error = player_move(&player);
    		if (error != ERROR_NONE) {
    			fruit_destroy(&fruit);
    			player_destroy(&player);
    			handle_error(error);
    			return EXIT_FAILURE;
    		}
    
    		if (player_eat(&player, &fruit)) {
    			player.growing = 10;
    			// todo: increase score and display
    		}
    
    		delay(player_is_moving_horizontally(&player) ? HDELAY : VDELAY);
    	} while (player_is_alive(&player) && !quit);
    
    	fruit_destroy(&fruit);
    	player_destroy(&player);
    	endwin();
    }
    


  • @DirkB Okay, also was genau ist Schrott?



  • @Swordfish Wow, wirklich vielen Dank! Ich verstehe allerdings nur wenig von der Syntax. Wo kann ich mehr darüber erfahren?



    • Mischen von deutschen und englischen Variablenamen
    • #defines für Konstanten (wenn es keinen Grund dafür gibt, sie als Präprozessorsymbole haben zu wollen).
    • Variablen nicht dort definiert, wo sie verwendet werden.
    • Falsche Signatur für main(). Laut Standard gibt es int main(void) und int main(int argc, char **argv)
    • int statt bool (siehe <stdbool.h> ab C99)
    • Alles Zeugs in ein Array schreiben anstatt die jeweiligen Änderungen auf den Bildschirm.
    • system("cls")
    • Einerseits write() und dann ein printf() nur um ein newline auszugeben.
    • Woher kommt getch() (In den eingebundenen Headern sollte es eigentlich nicht enthalten sein.)
    • Keine deiner Funktionen hat einen Rückgabetyp und nimmt eine unbestimmte Zahl an Parametern. (Ersteres ist seit ich glaube C90 nicht mehr erlaubt.)


  • @Swordfish Ah, Danke. Welcher Teil deines Codes ist dafür zuständig, das Array in das Terminal zu schreiben? Und warum brauchen Funktionen zwingend einen Rückgabetyp?



  • @daniel sagte in Schnellere Alternative zu printf, write und setchar:

    Welcher Teil deines Codes ist dafür zuständig, das Array in das Terminal zu schreiben?

    Ich habe kein solches Array wie Du in meinem Code.

    @daniel sagte in Schnellere Alternative zu printf, write und setchar:

    Und warum brauchen Funktionen zwingend einen Rückgabetyp?

    Weil es seit spätestens C89 so geschrieben steht.

    Function definitions (§3.7.1):

    [...] The return type of a function shall be void or an object type other than array.

    Davor wurde für Funktionen ohne Rückgabetyp int als Rückgabetyp angenommen.

    @daniel sagte in Schnellere Alternative zu printf, write und setchar:

    Ich verstehe allerdings nur wenig von der Syntax. Wo kann ich mehr darüber erfahren?

    Am besten währe wahrscheinlich, wenn Du Dir ein gutes Lehrbuch besorgst.



  • @Swordfish Habe ich schon. "C Programmieren von Anfang an" von Helmut Erlenkötter. Kannst du mir ein besseres empfehlen? Und wie speicherst du dann das Feld bzw wie gibst du den Spieler auf dem Terminal aus?



  • Ich habe ein "Fenster" an der Position des "Spielers":

    @Swordfish sagte in Schnellere Alternative zu printf, write und setchar:

    wclear(player->window);    // Fenster noch an der alten Position -> löschen
    wrefresh(player->window);  // ncurses mitteilen, daß das Fenster gezeichnet
                               // werden soll
    // Fenster an die aktuelle Position verschieben (mv ... move):
    mvwin(player->window, player->position.y, player->position.x);
    waddch(player->window, PLAYER_GLYPH);  // ein 'X' reinschreiben
    wrefresh(player->window);              // Auf den Bildschirm damit.
    

    Merken muss ich mir nur die Position. Ich muss auch nicht bei jeder Bewegung den ganzen Bildschirm löschen sondern nur die vorige Position des Spielers.

    @daniel sagte in Schnellere Alternative zu printf, write und setchar:

    Habe ich schon. "C Programmieren von Anfang an" von Helmut Erlenkötter. Kannst du mir ein besseres empfehlen?

    Der Erlenkötter ist auch eins der ersten gewesen, die mir eingefallen sind. Und da steht ernsthaft nicht drinnen, daß Funktionen einen Rückgabetyp haben und eine Parameterliste und eine Funktion die nichts zurückgibt und keine Parameter nimmt so ausschaut: void foo(void); ?



  • @daniel sagte in Schnellere Alternative zu printf, write und setchar:

    Habe ich schon. "C Programmieren von Anfang an" von Helmut Erlenkötter.

    Von wann ist denn das Buch?



  • @DirkB 1999



  • @Swordfish Es war davon die Rede, nur da ich keine Begründung dafür gefunden habe, habe ich es weggelassen



  • Ich schätze mal, Du verwendest den gcc? Dann kompiliere doch bitte mit -std=c99 -Wall -Wextra -Werror -pedantic



  • @Swordfish sagte in Schnellere Alternative zu printf, write und setchar:

    -std=c99 -Wall -Wextra -Werror -pedantic

    Warum?


Anmelden zum Antworten