Προς το περιεχόμενο

Παιχνίδι 2048 σε C


johnny.tifosi

Προτεινόμενες αναρτήσεις

Μόλις έριξα μια ματιά στο Mac Developer Library (section: Predefined Macros). Δεν υπάρχει __MACH... θέλει __APPLE && __MACH__ (δείτε το note που έχει).

 

Ούτε που θυμάμαι που το είχα βρει το __MACH... μπορεί να είναι gcc specific σε καμιά παλαιότερη έκδοση του gcc (οπότε με το __MACH__.zip που έβαλα στο προηγούμενο ποστ πρέπει να είμαστε οκ).

 

Fuzzy, όταν ευκαιρήσεις επιβεβαίωσε/διεύψευσε πως με αυτό παίζει οκ και στον Mac κι από αύριο/μεθαύριο θα πιάσω κα κάνω και τις προσθήκες στο game).

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Απαντ. 272
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

Συχνή συμμετοχή στο θέμα

Δημοσιευμένες Εικόνες

ΟΚ, αλλά τώρα...:

 

>delk@Pascal~/Downloads/2048cv$ clang -std=c99 -O2 -s my.c tui.c main.c
tui.c:51:3: warning: implicit declaration of function 'MY_DBGF' is invalid in
      C99 [-Wimplicit-function-declaration]
                MY_DBGF( "%s", "calloc failed!" );
                ^
1 warning generated.
main.c:408:3: warning: implicit declaration of function 'MY_DBGF' is invalid in
      C99 [-Wimplicit-function-declaration]
                MY_DBGF( "%s", "NULL pointer argument!" );
                ^
1 warning generated.
ld: warning: option -s is obsolete and being ignored
Undefined symbols for architecture x86_64:
  "_MY_DBGF", referenced from:
      _tui_printfxy in tui-17c67b.o
      _tui_draw_board in tui-17c67b.o
      _board_init in main-f593fe.o
      _board_has_room in main-f593fe.o
      _board_generate_tile in main-f593fe.o
      _board_has_hadjacent in main-f593fe.o
      _board_has_vadjacent in main-f593fe.o
      ...
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Σε έχω ταλαιπωρήσει, σόρι! Ανέβασα όλο το game ξανά στο προηγούμενο ποστ ως version 0.1b2

 

 

Τι έκανε πάλι η Εθνική; Μιλάμε για Τζέκιλ & Χάιντ... σήμερα ήταν χάρμα ιδέσθαι! Στις καλύτερες 16 του κόσμου (Ισπανία έξω, Αγγλία έξω, Ιταλία έξω... Ελλάδα μέσα!!!!)

 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δεν το έψαξα καθόλου, μπορεί να χρειάζεται καμιά βιβλιοθήκη.

 

Πάντως σε fedora 20 x64 με gcc 4.8.2 έβγαλε 2-3 errors.

 

attachicon.gifScreenshot from 2014-06-23 23:43:08.png

 

Αντίθετα με calng έγινε compile

 

attachicon.gifScreenshot from 2014-06-23 23:47:26.png

Oμοίως και εδώ με GCC (Clang δεν έχω).

 

H έκδοση του #21 μηνύματος όμως, πάει κανονικά (Fedora 20, 64bit, gcc  4.8.3 20140624 (Red Hat 4.8.3-1) ).

 

Thumbs up!

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

...

Thumbs up!

Thanks!

 

Πάντως δεν το έχω παρατήσει (παρόλο που πήζω). Κούτσου-κούτσου του έχω ενσωματώσει 4 παραλλαγές, του έχω βάλει skins (ένα dark κι ένα light) και σήμερα αν ευκαιρήσω θέλω να του ολοκληρώσω και το Undo (έχω φτιάξει ήδη την σχετική στοίβα, αλλά δεν την έχω ενσωματώσει ακόμα στη ροή του παιχνιδιού).

 

Τα παρακάτω ss στο spoiler είναι τα dark & light skins σε Windows mode (σε ANSI mode έχουν διαφορετικά χρώματα τα tiles, επειδή το minimum portable color-set στα ANSI terminals δεν υποστηρίζει ανοιχτόχρωμο background χρώμα)

 

 

 

Win32 Dark

 

post-38307-0-34364500-1404108129_thumb.png

 

Win32 Light

 

post-38307-0-88878500-1404108273_thumb.png

 

 

Εκείνο το Hint στις εντολές δεξιά, είναι για να μπει AI που θα μπορεί να δίνει hint στον παίκτη τι να παίξει, αλλά κατά 99,99% δεν με βλέπω να το φτιάχνω.

 

Μάλλον θα το αντικαταστήσω με κάνα Redo ή με κάνα Replay (αυτό το τελευταίο είναι εύκολο να γίνει... από την στιγμή που θα έχει υλοποιηθεί το Undo, όλες οι παιγμένες κινήσεις θα είναι αποθηκευμένες σε μια στοίβα, οπότε απλώς θα τις εφαρμόζω τη μια μετά την άλλη με κάποιο delay... το μόνο δύσκολο θα είναι να μπορεί να το σταματήσεις κανείς το replay όταν ξεκινήσει, γιατί ο κώδικας δεν είναι event-driven).

 

Γενικώς, όταν τελειώσω και το Undo, θα ποστάρω τον κώδικα.

 

Btw, το AI είναι καλή φάση αν θέλει κάποιος να το προσθέσει (κώδικας υπάρχει ήδη έτοιμος σε Javascript ενώ αν δείτε και τη σχετική συζήτηση στο StackOverflow θα βρείτε κι άλλες υλοποίησεις).

 

Ίσως είναι καλή ιδέα και για το νήμα με Ιδέες για C project. Αν ψηθούν περισσότεροι του ενός θα μπορούσαν να προστεθούν διάφορα gimmicks: GUI, 1 ή περισσότερες υλοποιήσεις AI, να ενσωματωθούν κι άλλες παραλλαγές του παιχνιδιού (υπάρχουν πολλές: http://2048game.com/variations/και διάφορες: http://allthe2048.com/ ... εγώ έχω ενσωματώσει τις 3 τελευταίες από το 1ο link, να αποθηκεύει scores (ενδεχομένως και online) κλπ, κλπ... ότι μπορούμε να φανταστούμε :)

 

Toy-project μεν, αλλά και μόδα είναι αυτές τις μέρες, και βατό είναι, και φάση έχει... γενικώς νομίζω έχει τα φόντα να προσελκύσει το ενδιαφέρον ακόμα και από παιδιά που δεν έχουν μεγάλη εμπειρία και θέλουν να τη διευρύνουν ανώδυνα ;)

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Λοιπόν, έβαλα και το Undo (με ποινή να μη κρατάει best-score μετά το 1ο undo... επίσης η τελευταία κίνηση δεν γίνεται undo).

 

Download: 2048cc_v02a.zip

 

Windows Skins Screenshots (με Lucidas Console font):

 

 

DARK:

post-38307-0-06718900-1404218697_thumb.png

 

LIGHT:

post-38307-0-22180400-1404218706_thumb.png

 

 

ANSI Skins Screenshots (από Ubuntu 11.04):

 

 

DARK:

post-38307-0-65723400-1404218718_thumb.png

 

LIGHT:

post-38307-0-07469300-1404218738_thumb.png

 

 

Things to-do

 

1. Redo (λέει πως δεν είναι ακόμα υλοποιημένο)

2. Hint (λέει πως δεν είναι ακόμα υλοποιημένο)

3. Save best-score (δεν υπάρχει καν σαν εντολή)

4. Replay (δεν υπάρχει καν σαν εντολή)

 

To Redo θα το φτιάξω, θέλει μια δικιά του στοίβα και ότι φεύγει από την στοίβα του undo να πηγαίνει στην στοίβα του redo, και το ανάποδο. Δεν το έκανα τώρα γιατί θέλει refactoring ο κώδικας, ώστε οι 2 στοίβες είτε να μπουν σε δικό τους struct (π.χ. MovesHstory) είτε να ενσωματωθούν μέσα στο υπάρχον GameState struct (θα δω). Τώρα η στοίβα του undo είναι χύμα.

 

Το Hint δεν προβλέπεται να το φτιάξω... τουλάχιστον όχι σύντομα. Ευχής έργον θα ήταν αν το έφτιαχνε κάποιος άλλος.

 

Την αποθήκευση του best-score σκέφτομαι να τη φτιάξω, αλλά λόγω του ότι το παιχνίδι υποστηρίζει 4 διαφορετικές παραλλαγές, πρέπει να γίνει αντίστοιχη μέριμνα κατά την αποθήκευση. Επίσης δεν είμαι πεπεισμένος πως ο τρόπος βαθμολόγησης είναι αξιοκρατικός (βασικά δεν το έχω ψάξει).

 

Το Replay μπορεί να το φτιάξω, μπορεί και όχι... βασικά δεν με καίει.

 

Πηγαίος κώδικας:

 

Τον κώδικα τον έχω χωρίσει ως εξής:

main.c
gs.c        (game-state & undo stack implementation)
board.c     (board implementation)
tui.c       (primitive x-platform text-user-interface implementation)
tui_skin.c  (skins implementation... used by tui.c)
my.c        (autonomous x-platform console utils)
common.h    (autonomous utility macros)
con_color.h/con_color_private.h (autonomous preprocessor interface)
Στα περισσότερα πηγαία αρχεία έχω συμπεριλάβει αρκετά αναλυτική τεκμηρίωση, αλλά υπάρχουν κάνα-δυο που δεν έχω προλάβει ακόμα (π.χ. gs.c/gs.h) ενώ υπάρχει περίπτωση κάποια από τα σχόλια να είναι λίγο outdated (νομίζω πως δεν είναι, αλλά you never know).

 

Σε γενικές γραμμές, τα source-modules αποτελούνται από private συναρτήσεις που συνήθως είναι inlined και δεν κάνουν sanity-checks (με τα ονόματά τους να ξεκινάνε με κάτω παύλα) και από full-blown public συναρτήσεις.

 

Επίσης συνήθως, το κάθε source-module κάνει expose publicly ένα ή περισσότερα opaque data-types (συνήθως ένα) τα οποία τα διαχειρίζονται τα υπόλοιπα modules μέσω αντίστοιχων pointers.

 

Έχω προσπαθήσει δηλαδή να κάνω ικανοποιητικό de-coupling μεταξύ των επιμέρους συστατικών του παιχνιδιού, με την ελπίδα πως ο κώδικας θα είναι πιο εύκολα scalable, και maintainable (μπορεί να βελτιωθεί κι άλλο όμως... προφανώς με κατάλληλο refactoring). Ελπίζω και κατανοητός.

 

Για όσους δεν θέλουν να κατεβάσουν το zip, παραθέτω μέσα σε spoiler τον κώδικα της main.c, στην οποία δεν υπάρχουν προφανώς private & public συναρτήσεις (κακώς δεν τις έχω ακόμα όλες static) αλλά δίνει γεύση για το γενικότερο setup που χρησιμοποιώ σε όλα τα πηγαία αρχεία...

 

 

 

/****************************************************************
 * This file is part of the "2048 Console Clone" game.
 *
 * Author:       migf1 <[email protected]>
 * Version:      0.2a
 * Date:         July 1, 2014
 * License:      Free Software (see following comments for limitations)
 * Dependencies: common.h, board.h, gs.h, tui.h
 * --------------------------------------------------------------
 *
 * Description
 * -----------
 *
 * A console clone of the game 2048 ( http://gabrielecirulli.github.io/2048/ )
 * written in ISO C99. It is meant to be cross-platform across Windows, Unix,
 * Linux, and MacOSX (for the latter 3, you should enable ANSI-colors support
 * on your terminal emulation).
 *
 * Compared to the original game, this version additionally supports skins
 * (color themes) and undo/redo functionality (with the penalty of disabling
 * best-score tracking).
 *
 * Moreover, in its current form, this version of the game is additionally
 * cloning 3 unofficial variations of the original game, namely:
 *
 * - 5x5 board ( http://2048game.com/variations/5x5.html )
 * - 6x6 board ( http://2048game.com/variations/6x6.html )
 * - 8x8 board ( http://2048game.com/variations/8x8.html )
 *
 * License
 * -------
 *
 * The game is open-source, free software with only 3 limitations:
 *
 * 1. Keep it free and open-source.
 * 2. Do not try to earn any kind of profit from it or from any
 *    derivatives of it, unless you have contact me and we have
 *    made special arrangements ( [email protected] ).
 * 3. Always re-distribute the original package, along with any
 *    software you distribute that is based on this game.
 ****************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#include "common.h"   /* DBGF() */
#include "board.h"    /* board related functions */
#include "gs.h"       /* game-state */
#include "tui.h"      /* text-user-interface */

/* Macro for validating a key as a command
 * for starting a new variant of the game.
 */
#define VALID_VARIANT_KEY(key)      \
(                                   \
	TUI_KEY_BOARD_4 == (key)    \
	|| TUI_KEY_BOARD_5 == (key) \
	|| TUI_KEY_BOARD_6 == (key) \
	|| TUI_KEY_BOARD_8 == (key) \
)

/* --------------------------------------------------------------
 * int do_play_board():
 *
 * Play the move indicated by the key argument, on the board of the
 * specified game-state (gs), and update accordingly the game-state,
 * the specified text-user-interface (tui), and the specified undo
 * stack.
 *
 * Return 1 (true) if the move caused game-over, 0 (false) otherwise.
 *
 * NOTE: A game is over either when a tile reaches the sentinel-value
 *       (iswin) or when the board is full and there are no adjacent
 *       tiles with equal, non-0, values.
 * --------------------------------------------------------------
 */
int do_play_board( int key, GameState *gs, Tui *tui, GSNode **undo )
{
	int moved = 0;    /* was a move successfully played? */
	int iswin = 0;    /* was a winning move played? */

	/* just for brevity later on */
	Board *board;
	long int score, bscore;
	int didundo;

	if ( NULL == gs || NULL == tui || NULL == undo ) {
		DBGF( "%s", "NULL pointer argument!" );
		return 1;
	}

	/* do the move */
	board  = gamestate_get_board( gs );
	score  = gamestate_get_score( gs );
	bscore = gamestate_get_bestscore( gs );
	switch( key )
	{
		case TUI_KEY_UP:
			moved = board_move_up( board, &score, &iswin );
			break;

		case TUI_KEY_DOWN:
			moved = board_move_down( board, &score, &iswin );
			break;

		case TUI_KEY_LEFT:
			moved = board_move_left( board, &score, &iswin );
			break;

		case TUI_KEY_RIGHT:
			moved = board_move_right( board, &score, &iswin );
			break;
		}

	/* update score, and if needed best-score too */
	didundo = gamestate_get_didundo( gs );
	gamestate_set_score(gs, score);
	if ( !didundo && bscore < score ) {
		gamestate_set_bestscore( gs, score ) ;
	}

	/* was it a winning move? */
	if ( iswin ) {
		tui_draw_infobar_winmsg( tui );
	}
	/* moved but not won? */
	else if ( moved ) {
		board_generate_ntiles(
			board,
			board_get_nrandom( board ) 
			);
		gsstack_push( undo, gs );
	}

	/* is current game over? */
	return iswin
	       ||
	       !( board_has_room(board) || board_has_adjacent(board) )
	       ;
}

/* --------------------------------------------------------------
 * void do_reset_game():
 *
 * Start a new game, keeping the current settings of the specified
 * game-state object (gs). Update accordingly the specified text-
 * -user-interface (tui) and the specified undo stack.
 * --------------------------------------------------------------
 */
void do_reset_game( GameState *gs, Tui *tui, GSNode **undo )
{
	if ( NULL == gs || NULL == tui || NULL == undo ) {
		DBGF( "%s", "NULL pointer argument!" );
		return;
	}

	if ( 'y' == tolower( tui_draw_iobar_prompt_newgame(tui)) ) {
		gamestate_reset( gs );

		gsstack_free( undo );
		gsstack_push( undo, gs );

		tui_clear_infobar( tui );
	}
}

/* --------------------------------------------------------------
 * void do_new_variant():
 *
 * Start a new variant of the game, according to the specified key.
 * The specified game-state (gs), text-user-interface (tui) and undo
 * stack, are updated accordingly.
 *
 * NOTES: ( IMPORTANT! )
 *
 *        Launching a new variant of the game, is usually requiring
 *        to also resize the board. Resizing is done in the function
 *        board_resize_and_reset(), via realloc(), which does NOT
 *        guarantee that the resized board will stay at the same
 *        location in memory.
 *
 *        Thus, it is IMPORTANT to call the function:
 *        tui_update_board_reference() AFTER the board has been
 *        resized, so the text-user-interface gets aware of the
 *        new memory location of the resized board.
 * --------------------------------------------------------------
 */
void do_new_variant( int key, GameState *gs, Tui *tui, GSNode **undo )
{
	int dim = key - '0';   /* the new single-dimension of the board */
	Board *board;          /* just for brevity later on */

	if ( NULL == gs || NULL == tui || NULL == undo ){
		DBGF( "%s", "NULL pointer argument!" );
		return;
	}
	if ( !VALID_VARIANT_KEY(key) ){
		DBGF( "%c (%d) is not a valid variant key!", key, key );
		return;
	}

	board = gamestate_get_board( gs );
	if ( dim != board_get_dim(board) )
	{
		if ( 'y' == tolower( tui_draw_iobar_prompt_newgame(tui)) ) {
			board_resize_and_reset( board, dim );
			board_generate_ntiles(
				board,
				2 * board_get_nrandom( board )
				);

			gamestate_set_score( gs, 0 );
			gamestate_set_didundo( gs, 0 );  /* false */

			gsstack_free( undo );
			gsstack_push( undo, gs );

			tui_update_board_reference( tui, board );
			tui_cls( tui );
		}
	}
}

/* --------------------------------------------------------------
 * void do_undo():
 *
 * Undo the last move on the board, and update accordingly the
 * specified game-state (gs), text-user-interface (tui) and the
 * undo stack.
 *
 * NOTES: The very first move (the random generation of the initial
 *        tiles) is done automatically by the game, so it cannot be
 *        undone.
 *
 *        The first time the player tries to undo his move, he is
 *        asked for confirmation because undo cancels the recording
 *        of the best-score. Subsequent undoing is done without
 *        confirmation.
 *
 *        Best-score cancellation is achieved via a boolean flag
 *        inside the specified game-state (gs), called: didundo.
 *        If the player confirms the first undo, this flag is set
 *        to 1 (true). The function do_play_board() which updates
 *        the best-score, does so only if this flag is set to 0
 *        (false). 
 * --------------------------------------------------------------
 */
void do_undo( GameState *gs, Tui *tui, GSNode **undo )
{
	const GSNode *back;   /* previous game-state in the undo stack */

	if ( NULL == gs || NULL == tui || NULL == undo ) {
		DBGF( "%s", "NULL pointer argument!" );
		return;
	}

	/* 1st move was done automatically by the game, so ignore it */
	if ( NULL == *undo || gsstack_peek_count(*undo) < 2 ) {
		tui_sys_beep(1);
		return;
	}

	/* the very 1st time warn the user that undo will cancel best-score */
	if ( 0 == gamestate_get_didundo(gs) ) {
		if ( 'y' != tolower(tui_draw_iobar_prompt_undo(tui)) ){
			return;
		}
	}

	gsstack_pop( undo );

	back = gsstack_peek( *undo );
	if ( NULL != back ) {
		gamestate_copy(
			gs,
			gsstack_peek_state(back)
			);
	}

	gamestate_set_didundo(gs, 1); /* true */
}

/* --------------------------------------------------------------
 * void do_cycle_skin():
 *
 * Apply the next available skin of the specified tui object.
 *
 * NOTE: The skins and their order are fixed. They are enabled
 *       automatically, during the creation of the tui object.
 *       They are implemented separately in the source-module:
 *       tui_skin.c (which is used by tui.c).
 * --------------------------------------------------------------
 */
void do_cycle_skin( Tui *tui )
{
	if ( NULL == tui ){
		DBGF( "%s", "NULL pointer argument!" );
		return;
	}

	tui_cycle_skin( tui );
	tui_cls( tui );
}

/* --------------------------------------------------------------
 * void cleanup():
 *
 * Release the memory reserved for the specified game-state (gs),
 * text-user-interface (tui) and undo stack.
 * --------------------------------------------------------------
 */
void cleanup( GameState *gs, Tui *tui, GSNode *undo )
{
	tui_release( tui );
	gamestate_free( gs );
	gsstack_free( &undo );
}

/* --------------------------------------------------------------
 * void alloc():
 *
 * Allocate and initialize to default values the memory initially
 * needed at game launch for the specified game-state (gs), and
 * text-user-interface (tui). Return 0 (false) on error, 1 (true)
 * otherwise.
 *
 * Upon success, the addresses of the specified game-state and the
 * text-user-interface, are pointing to the newly created objects
 * in memory.
 *
 * NOTES: The default values for the creation of both the game-state
 *        and the text-user-interface, are the ones corresponding to
 *        the classic, 4x4 board, version of the game.
 * --------------------------------------------------------------
 */
int alloc( GameState **gs, Tui **tui )
{
	Board *board;       /* just for brevity later on */

	if ( NULL == gs || NULL == tui ) {
		DBGF( "%s", "NULL pointer argument " );
		return 0;
	}

	*gs = new_gamestate( BOARD_DIM_4 );
	if ( NULL == *gs ) {
		return 0;
	}

	board = gamestate_get_board( *gs );

	*tui = new_tui( *gs );
	if ( NULL == *tui ) {
		board = board_free( board );
		return 0;
	}

	return 1;
}

/* --------------------------------------------------------------
 * Application's entry point.
 * --------------------------------------------------------------
 */
int main( void )
{
	int gameover  = 0;            /* is current game over? */
	int key       = '\0';         /* user keypress (see my.h) */
	unsigned int keymask = 0x00;  /* indicates Arrows and/or FKeys */
	Tui *tui      = NULL;         /* text user interface */
	GSNode *undo  = NULL;         /* undo stack (stores game-states) */
	GameState *gs = NULL;         /* current game-state */

	/* allocate initially needed memory */
	if ( !alloc(&gs, &tui) ) {
		exit( EXIT_FAILURE );
	}

	srand( time(NULL) );

	gamestate_reset( gs );
	gsstack_push( &undo, gs );

	/* game loop */
	for (;
	{
//		dbg_gsnode_dump( undo );

		gameover = 0;    /* reset to false */

		tui_redraw( tui	);

		key = toupper( tui_sys_getkey(&keymask) );

		/* esc or quit key */
		if ( TUI_KEY_ESCAPE == key || TUI_KEY_QUIT == key ) {
			break;
		}
		/* arrow key */
		else if ( keymask & TUI_KEYMASK_ARROW ) {
			gameover = do_play_board( key, gs, tui, &undo );
		}
		/* cycle-skin key */
		else if ( TUI_KEY_SKIN == key ) {
			do_cycle_skin( tui );
		}
		/* new-game key */
		else if ( TUI_KEY_RESET == key ) {
			do_reset_game( gs, tui, &undo );
		}
		/* new-variant key */
		else if ( VALID_VARIANT_KEY(key) ) {
			do_new_variant( key, gs, tui, &undo );
		}
		/* undo key */
		else if ( TUI_KEY_UNDO == key ) {
			do_undo( gs, tui, &undo );
		}
		/* redo key */
		else if ( TUI_KEY_REDO == key ) {
			tui_draw_iobar_prompt_notyet( tui );
		}
		/* hint key */
		else if ( TUI_KEY_HINT == key ) {
			tui_draw_iobar_prompt_notyet( tui );
		}

		/* is current game over? */
		if ( gameover ) {
			tui_draw_board( tui );
			tui_draw_scoresbar( tui );

			tui_sys_beep(1);
			tui_draw_iobar_prompt_gameover( tui );
			if ( 'y' == tolower( tui_draw_iobar_prompt_newgame(tui)) ) {
				gamestate_reset( gs );
				gsstack_free( &undo );
				gsstack_push( &undo, gs );
				tui_clear_infobar( tui );
			}
			else {
				break;
			}
		}

	}

	cleanup( gs, tui, undo );
	exit( EXIT_SUCCESS );
}

 

Όπως φαίνεται στην αρχή της main(), τα gamestate (gs), text user interface (tui) και η undo-stack (undo) ορίζονται ως δείκτες στα αντίστοιχα opaque data-types. Κατόπιν διαχειρίζονται παντού έμμεσα, μέσω συναρτήσεων που ξεκινάνε με "gamestate_", "tui_" και "gsstack_", αντίστοιχα.

 

Compilation:

 

Μπαίνουμε από την γραμμή εντολών στον φάκελο src, και κατόπιν...

 

α) σε Windows/Cygwin με (mingw)gcc:

gcc -std=c99 -s -O3 *.c -o 2048cc.exe
β) σε Unix/Linux/MacOSX με (mingw)gcc:

gcc -std=c99 -s -O3 *.c -o 2048cc.out
Αλλιώς, σε οποιαδήποτε πλατφόρμα με χρήση IDE/compiler combo, προσθέτουμε όλα τα *.c αρχεία σε ένα project, ενεργοποιούμε την υποστήριξη C99 στον compiler, και κάνουμε Build & Run.

 

Έτοιμα Εκτελέσιμα:

 

Στο zip στην αρχή του ποστ, έχω συμπεριλάβει έτοιμα 32μπιτα εκτελέσιμα για Windows και Ubuntu 11.04

 

Disclaimer

 

Εννοείται πως δεν έχω καμιά ευθύνη για οποιοδήποτε πρόβλημα προκληθεί στο σύστημά σας από τη χρήση του παιχνιδιού :P

 

Επίσης, είναι πολύ πιθανό ο κώδικας να περιέχει αβλεψίες ή/και bugs.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Καταλάθος πήρε το μάτι μου το thread και είπα να ρίξω μια ματιά. Τελικά το αποτέλεσμα του προγράμματος που φτιάξατε είναι πολύ καλό. Μπράβο σε όσους ασχολήθηκαν!

 

Μια απορία έχω. Η C υποστηρίζει και την δημιουργία γραφικών? Δεν έχω ιδέα πως γίνεται αυτό. :P

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Μια απορία έχω. Η C υποστηρίζει και την δημιουργία γραφικών? Δεν έχω ιδέα πως γίνεται αυτό. :P

Η C όπως ορίζεται από το πρότυπο όχι. Υπάρχουν λύσεις σε χαμηλό επίπεδο για γραφικά καθώς και βιβλιοθήκες που κάνουν τη δουλειά για εσένα αλλά η επιλογή σου εξαρτάται από το λειτουργικό που χρησιμοποιείς.

 

Συγκεκριμένα το παρόν πρόγραμμα, από όσο βλέπω στις screenshots, δεν χρησιμοποιεί "γραφικά" αλλά κείμενο χρωματισμένο με τη χρήση ansi sequences ή κάτι παρόμοιο το οποίο και πάλι εξαρτάται από το λειτουργικό :)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Καταλάθος πήρε το μάτι μου το thread και είπα να ρίξω μια ματιά. Τελικά το αποτέλεσμα του προγράμματος που φτιάξατε είναι πολύ καλό. Μπράβο σε όσους ασχολήθηκαν!

Ευχαριστούμε :)

 

Παραθέτω και την έκδοση 0.2a2 η οποία έχει υλοποιημένα τα Redo και Replay, και της έχω βάλει να δείχνει και έναν μετρητή των κινήσεων.

 

Download: 2048cc_v02a2.zip (κώδικας & 32μπιτο εκτελέσιμο Windows)

 

 

 

post-38307-0-59919500-1404342836_thumb.png

 

 

Οι οδηγίες για compile είναι ίδιες με του προηγούμενου ποστ μόνο που έχει προστεθεί και το αρχείο: mvhist.c (moves history).

 

Δεν το έχω τεστάρει επαρκώς, οπότε μπορεί να περιέχει καμιά χοντράδα... αν αντιμετωπίσετε κανένα πρόβλημα, γράψτε εδώ να το κοιτάξω. Επίσης, δεν έχω βάλει σχόλια στα έξτρα, συγκριτικά με την έκδοση 0.2a.

 

Επόμενο πλάνο (εάν και όταν), αποθήκευση και φόρτωμα replays (ενδεχομένως και παιχνιδιού για συνέχιση). Να μπορούμε δηλαδή να αποθηκεύουμε τα replays καλών μας προσπαθειών και να μπορούν οι άλλοι να τα φορτώνουν για να τα παρακολουθήσουν.

 

Μια απορία έχω. Η C υποστηρίζει και την δημιουργία γραφικών? Δεν έχω ιδέα πως γίνεται αυτό. :P

Εννοείται πως ναι, μέσω αμέτρητων βιβλιοθηκών. Το συγκεκριμένο, όπως ήδη ειπώθηκε, δεν χρησιμοποιεί γραφικά αλλά χρωματισμένο κείμενο, μέσω ενός απλού pre-processor interface που έχω φτιάξει ειδικά για αυτή τη δουλειά. Περιέχεται μέσα στα zip (con_color.h/con_color_private.h) και δουλεύει σε Windows, Unix, Linux και MacOSX.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

@migf1

Δεν φανταζόμουν που θα έφτανε το thread αυτό όταν το άνοιγα :-D Συγχαρητήρια, εσύ έχεις πάει το πρόγραμμα 10 επίπεδα παραπάνω. Δυστυχώς με τις βασικές μου γνώσεις δεν μπορώ καν να καταλάβω τον κώδικά σου. Πάντως μου έδωσες την ιδεα να ενσωματώσω στον δικό μου best score και undo.

  • Like 2
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Εννοείται πως ναι, μέσω αμέτρητων βιβλιοθηκών. Το συγκεκριμένο, όπως ήδη ειπώθηκε, δεν χρησιμοποιεί γραφικά αλλά χρωματισμένο κείμενο, μέσω ενός απλού pre-processor interface που έχω φτιάξει ειδικά για αυτή τη δουλειά. Περιέχεται μέσα στα zip (con_color.h/con_color_private.h) και δουλεύει σε Windows, Unix, Linux και MacOSX.

 

Η C όπως ορίζεται από το πρότυπο όχι. Υπάρχουν λύσεις σε χαμηλό επίπεδο για γραφικά καθώς και βιβλιοθήκες που κάνουν τη δουλειά για εσένα αλλά η επιλογή σου εξαρτάται από το λειτουργικό που χρησιμοποιείς.

 

Συγκεκριμένα το παρόν πρόγραμμα, από όσο βλέπω στις screenshots, δεν χρησιμοποιεί "γραφικά" αλλά κείμενο χρωματισμένο με τη χρήση ansi sequences ή κάτι παρόμοιο το οποίο και πάλι εξαρτάται από το λειτουργικό :)

 

Και εγώ που θεωρούσα την C πολύ φτωχή σε δυνατότητες λόγω της έλλειψης αντικειμενοστράφειας! :P

Σας ευχαριστώ για τις απαντήσεις.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

@tifosi:

 

Ευχαριστώ για τα καλά λόγια. Το πρόβλημα είναι πως αφενός δεν έχω τον απαιτούμενο ελεύθερο χρόνο για να ασχοληθώ όσο θα ήθελα, και αφετέρου πως με εξαίρεση τον geomagas (βασικά δεν κατάλαβα αν θέλεις να προχωρήσεις τον κώδικα ή απλά να το κάνεις host σε κάποιο cvs ) κανείς άλλος δεν ενδιαφέρθηκε να ασχοληθεί περαιτέρω.

 

Μέσα στο ΣΚ θα δω μήπως καταφέρω να του βάλω load/save replay, γιατί νομίζω είναι καλή φάση να μπορούμε να βλέπουμε games άλλων.

 

Αλλά είναι πιο δύσκολο στην πράξη από ότι στα λόγια, γιατί μεταξύ διάφορων άλλων, για να είναι portable τα αρχεία πρέπει να είναι σε μορφή απλού κειμένου και προφανώς να υπάρχει και κάποιο (de)serialization των διάφορων data-structures.

 

Προς το παρόν του έχω φτιάξει ένα replay-interface όπου εκτός από αυτόματη αναπαραγωγή μπορεί κανείς να κινηθεί και χειροκίνητα με τα βελάκια μπρος-πίσω, καθώς και με τα Home/End για άμεση μετάβαση στην 1η και στην τελευταία καταγεγραμμένη κίνηση (αλλά τα Home/End δεν είχα χώρο να τα δείξω στην οθόνη, οπότε δουλεύουν... κρυφά :lol:). Παραθέτω σχετικό ss σε spoiler, αλλά κώδικα λέω να ποστάρω όταν θα δουλεύουν και τα Load / Save...

 

 

 

post-38307-0-16311400-1404558665_thumb.png

 

 

Εκείνο το 16 κάτω-δεξιά λέει πως αυτή τη στιγμή βλέπουμε την κίνηση 16.

 

@thomason:

 

Θα ήταν αδύνατον να είναι φτωχή σε δυνατότητες η γλώσσα πάνω στην οποία πατάει ολάκερο το στερέωμα της πληροφορικής ;)

 

Μιας και ανέφερες την αντικειμενοστράφεια, τα tui_skin.c, tui.c, board.c, gs.c και mvhist.c, είναι φτιαγμένα με αντικειμενοστραφή λογική. Φαντάσου το καθένα από αυτά πως περιέχει τις public & private μεθόδους της κλάσης που αντιπροσωπεύει, η οποία γίνεται διαθέσιμη μέσω των opague data-types που κάνουν expose publicly τα .h interfaces αυτών των αρχείων.

 

Εξωτερικά, το instantiation της κάθε κλάσης γίνεται με opaque δείκτες προς την εκάστοτε κλάση. Για παράδειγμα, για να κάνεις instantiate ένα object της κλάσης MovesHistory, ας πούμε στην main() γράφεις κάτι σαν το παρακάτω:

 

#include "mvhist.h"

int main( void )
{
    MovesHistory *mvhist = new_mvhist();
    ...
    mvhist_reset( mvhist );
    ...
    ...
    mvhist_free( mvhist );

    return 0;
}
Απλώς πρέπει να καλέσεις χειροκίνητα και τον destructor mvhist_free().

 

Αν δεν το είχα φτιάξει εξαρχής το game με αντικειμενοστραφή λογική, κατά πάσα πιθανότητα θα το είχα παρατήσει εδώ και πολύ καιρό ή τουλάχιστον δεν θα του πρόσθετα συνεχώς νέα πράγματα με ευκολία. Παρόλο που λόγω έλλειψης σοβαρού αρχικού σχεδιασμού, έχω κάνει αρκετές φορές refactor τον κώδικα (π.χ. για να φτιάξω το replay-interface που δείχνω στο ss άλλαξα την στοίβα από single σε doubly linked-list και την έκανα iterable).

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

Download: 2048cc_v03a1.zip

Download: 2048cc_v03a.zip

 

EDIT: Η έκδοση 0.3a είχε bug στα 6x6 ταμπλό (έμπαινε σε infinite-loop όταν γέμιζε το ταμπλό). Ανέβασα έκδοση 0.3a1 η οποία λογικά διορθώνει το bug (ελπίζω να μη δημιούργησε άλλα :lol:). Στην 0.3a1 δεν υπάρχει εκτελέσιμο για Ubuntu (μόνο για Windows υπάρχει) αλλά περιέχει ένα πρόχειρο Readme.txt καθώς επίσης κι ένα replay με ταμπλό 6x6 με σχεδόν γεμάτες όλες τις θέσεις, για περαιτέρω δοκιμές.

 

Το zip αρχείο περιέχει κώδικα, screen-shots,  32μπιτα εκτελέσιμα για Windows και Ubuntu 11.04 και ένα replay αρχείο φτασμένο μέχρι το 1024 για να το συνεχίσει όποιος θέλει (ss στο spoiler)

 

 

 

post-38307-0-06933100-1404763542_thumb.png

 

 

Το 366/366 κάτω-δεξιά λέει πως αυτή τη στιγμή βλέπουμε την τελευταία καταγεγραμμένη κίνηση του relay, ενώ εκείνο το UP λέει πώς προέκυψε αυτή η κίνηση από την προηγούμενη (δηλαδή παίχτηκε προς τα επάνω το ταμπλό στην προηγούμενη κίνηση).

 

Compilation:

 

Πάμε στον φάκελο src/ από γραμμή εντολών και για gcc γράφουμε:

gcc -std=c99 -s -O3 -D_BSD_SOURCE *.c
Προσαρμόστε το ανάλογα για άλλους compilers. Αν έχετε IDE+compiler, φτιάξτε ένα project με όλα τα .c αρχεία του φακέλου src/ και ενεργοποιήστε την C99. Επίσης, αν είστε σε linux με gcc, ορίστε το directive _BSD_SOURCE

 

Μόλις βγει το εκτελέσιμο, βγάλτε έξω από τον φάκελο src/ (βασικά πρέπει να βρίσκεται στην ίδια διαδρομή με τον φάκελο replays/).

 

Στις προσθήκες από την προηγούμενη έκδοση έχω βάλει ελάχιστα σχόλια, ενώ είναι πολύ πιθανόν να υπάρχουν και bugs. Όποιος έχει την ευγενή καλοσύνη ας το δοκιμάσει κι ότι βρει ας το ποστάρει εδώ.

 

Things TO-DO (όχι απαραίτητα από εμένα :P):

 

1. Refactoring του κώδικα, γιατί με τα replays και το (de)serialization τους νομίζω έγινε λίγο... μπάχαλο.

 

2. Προσθήκη σχολίων στον κώδικα και στοιχειώδη έστω τεκμηρίωση (π.χ. Readme αρχείο).

 

3. Αποθήκευση των best-score ανάλογα με τις διαστάσεις του ταμπλό.

 

4. Βελτίωση του τρόπου επιλογής των replay-files για φόρτωμα/σώσιμο. Όπως είναι τώρα, σώζει και φορτώνει μονάχα από τον φάκελο replays/ ο οποίος πρέπει να βρίσκεται όπου και το εκτελέσιμο. Επίσης, κατά το σώσιμο, δεν δίνεται επιλογή για την ονομασία του αρχείου, αλλά το φτιάχνει αυτόματα με το timestamp της στιγμής που γίνεται η αποθήκευση (π.χ. "Mon_Jul_07_225558_2014.sav" όπου "225558" είναι η ώρα: 22:55.58)... Για να το φορτώσουμε πρέπει να το πληκτρολογήσουμε ολόκληρο (εγώ πάω και τα κάνω rename χειροκίνητα μέσα στον φάκελο replays :P)

 

5. Προθήκη x-platform GUI, κατά προτίμηση GTK+ (αυτό θα λύσει εν πολλοίς και τα προβλήματα του 4).

 

6. Προσθήκη κι άλλων παραλλαγών του αυθεντικού παιχνιδιού (ορισμένες προϋποθέτουν αρκετές αλλαγές... π.χ. τώρα τα random generated tiles έχουν ή 2 ή 4... κάποιες παραλλαγές βγάζουν μόνο 2, η άλλα νούμερα).

 

7. Προσθήκη AI, είτε για να παίζει μόνο του, είτε καλύτερα για να δίνει hints στον player κατά βούληση (ιδανικά να μπουν και 2-3 επίπεδα "αυθεντίας" του AI, ανάλογα το ύψος του game-tree που θα δημιουργεί).

 

8. Random access στις κινήσεις των replays.

 

Replays:

 

Μπορείτε να τα χρησιμοποιήσετε και για να σώζετε μισοτελειωμένα παιχνίδια, με σκοπό να τα συνεχίσετε αργότερα, ως εξής.

 

Μπείτε σε "replay mode" και πατήστε 'S' για να το σώσετε (όταν μπαίνετε σε "replay-mode" σας γυρίζει αυτόματα στην 1η κίνηση... μπορείτε αν θέλετε να πατήσετε End για να πάτε στην τελευταία).

 

Απλώς να θυμάστε πως ως τελευταία κίνηση λογίζεται η τελευταία που έχετε κάνει Undo (αν έχετε κάνει). Οπότε, αν έχετε κάνει διάφορα Undo, ίσως θέλετε πρώτα να κάνετε αντίστοιχα Redo πριν μπείτε σε "replay-mode"

 

Όταν βγαίνετε από το "replay-mode" σας γυρίζει αυτόματα στην τελευταία κίνηση που είχατε παίξει (ισχύουν τα ίδια με παραπάνω σχετικά με τα Undo).

 

Αρχεία των Replay:

 

Τα αρχεία των replay είναι serialized αρχεία απλού κειμένου, με Windows τελειώματα γραμμών (\r\n). Δηλαδή το παιχνίδι τα σώζει έτσι, ανεξάρτητα με το σε ποια πλατφόρμα έγινε compile. Όταν τα φορτώνετε μέσα στο παιχνίδι, τα μετατρέπει αυτόματα στα τελειώματα που χρησιμοποιεί η πλατφόρμα που έγινε compile το εκτελέσιμο (δηλαδή περίπου, όχι ακριβώς... η σχετική συνάρτηση είναι η s_fixeol() στο αρχείο common.c... αν βρείτε χρόνο δοκιμάστε το και πείτε μου αν δουλεύει σωστά και σε MacOSX... σε Win & Ubuntu το ψιλοδοκίμασα και δείχνει να είναι ΟΚ)

 

To serialization των αρχείων γίνεται βάσει της δομής MovesHistory που ορίζεται στο αρχείο: mvhist.c, αλλά χρησιμοποιεί συναρτήσεις από διάφορα αρχεία (εκείνες που τελειώνουν σε _to_text() ) επειδή η MovesHistory περιέχει opaque data-types, οπότε το implementation του εκάστοτε opaque data-types ορίζεται στα αρχικά .c αρχεία που τους αντιστοιχούν.

 

Το de-serialization γίνεται με τις συναρτήσεις: new_xxx_from_text() μόνη εξαίρεση την: new_mvhist_from_file().

 

Το παρακάτω είναι ένα serialized αρχείο-replay που περιέχει 4 κινήσεις:

0
4:8 8 1@4 2048 1 12 1#0 0 2 2 0 0 0 8 2 0 0 0 0 0 0 0 
3:0 0 4@4 2048 1 12 0#0 0 0 2 0 0 0 4 0 0 0 0 0 0 2 4 
2:0 0 2@4 2048 1 13 0#0 2 0 0 0 0 0 0 0 0 0 0 2 0 4 0 
1:0 0 0@4 2048 1 14 0#2 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 
NULL:
750 4 4
4:0 0 0@4 2048 1 14 0#2 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 
3:0 0 2@4 2048 1 13 0#0 2 0 0 0 0 0 0 0 0 0 0 2 0 4 0 
2:0 0 4@4 2048 1 12 0#0 0 0 2 0 0 0 4 0 0 0 0 0 0 2 4 
1:8 8 1@4 2048 1 12 1#0 0 2 2 0 0 0 8 2 0 0 0 0 0 0 0 
Το 0 στην αρχή είναι boolean, και λέει πως ο παίκτης ΔΕΝ έχει κάνει Undo.

Μετά ακολουθούν τα 4 nodes της Undo στοίβας. Κατόπιν, to NULL: σημαίνει πως δεν υπάρχει Redo στοίβα (αν υπήρχε, θα εμφανίζονταν εκεί τα δικά της nodes) και κατόπιν ακολουθεί το replay-structure (μια γραμμή με meta-data και μετά τα nodes της replay στοίβας... που βασικά είναι η Undo στοίβα αντεστραμμένη).

 

Αυτά για σήμερα. Όποιος βρει χρόνο και όρεξη, ας δοκιμάσει για bugs, να τα φτιάξω αν είναι.

Επεξ/σία από migf1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα

  • Δημιουργία νέου...