Kakuro

Description

Voila, je bosse sur un projet pour mes cours de programmation.
Le premier projet consistait en une grille statique realisée correctement.
Pour le second projet, mon prof veut un tableau dynamique. Je crois que j'en suis pas loin mais ca plante pour l'affichage et paf l'erreur habituelle "Erreur de segmentation". Vous pouvez tester le code en mettant en commentaire de la ligne 82 et la ligne 100 et la miracle ca marche. Mais ca ne m'arrange pas du tout puisque mes cases sont vides :(.
Je sais que j'ai defini ici ma taille du tableau par nx,ny mais je dois en fait recuperer la taille en lisant la grille du kakuro, et la aussi je seche, j'utilise un "sscanf" pour lire dans le fichier, il me recupère bien la valeur mais ne l'attribue pas.
Dans un premier temps, je cherche uniquement a resoudre mon problème d'affichage car après vérification, la memoire est belle est bien allouée.
Bon, ne jugez pas mon code lol, je suis novice en programmation et je trouve que le prof a fait un peu fort de demander un kakuro pour des premières années, mais je debrouille meme si parfois je pourrais faire un meilleur codage. Je vous remercie a tous d'avance.
Je joinds le zip comme ca vous avez une grille, donc quand vous jouer, vous tapez 1 puis 1 et encore 1 car c'est la grille11.txt

Source / Exemple :


#include<stdio.h>
#include<stdlib.h>
#include"couleurs.h"

typedef struct element
{
	char type;
	int val[2];
	int attrib;
}grille;

/* Explication de la fonction INIT */
/* Fonction servant a initialiser le tableau pour eviter des problemes d'allocation */
/* de valeur dans les cases, pouvant causer des erreurs */

void init(int nx,int ny,grille **tab)
{
	int x,y;
	for(x=0;x<nx;x++)
	{
		for(y=0;y<ny;y++)
		{
			tab[x][y].type='Z';
			tab[x][y].val[0]=0;
			tab[x][y].val[1]=0;
			tab[x][y].attrib=-1;
		}
	}
}

/* Explication de la fonction AFFICHAGE */
/* En fonction du type attribue a chaque case, un affichage se distinque par le switch */
/* Le "default" equivalant aux cases blanches non-codees */

void affichage(int nx,int ny,grille **tab)
{
	int x,y,x2;
	printf("\033[H\033[2J");
	for(x=0;x<nx+1;x++)
	{
		couleur("31");
		printf("+-----");
		if(x==nx)
		{
			printf("+\n");
		}
	}
	for(x=0;x<nx;x++)
	{
		if(x==0)
		{
			printf("|     ");
		}
		printf("| %2d  ",x);
		if(x==nx-1)
		{
			printf("|\n");
		}
	}
	for(x=0;x<nx;x++)
	{
		for(x2=0;x2<nx+1;x2++)
		{
			couleur("31");
			printf("+-----");
			if(x2==nx)
			{
				printf("+\n");
			}
		}		
		for(y=0;y<ny;y++)
		{
			if(y==0)
			{
				couleur("31");
				printf("| %2d  ",x);
				couleur("0");
			} 
	 		couleur("31");	
			printf("|");
			couleur("0");
			switch (tab[x][y].type)
			{
				case 'D' : couleur("36");
					   printf("%2d\\%2d",tab[x][y].val[1],tab[x][y].val[0]);
					   couleur("0");
				  	   break;
				case 'N' : couleur("32");
					   printf("*****");
					   couleur("0");
					   break;
				case 'P' : couleur("33");
					   printf(" %2d  ",tab[x][y].val[0]);
					   couleur("0");
					   break;
				case 'B' : printf(" %2d  ",tab[x][y].val[0]);
					   break;
				default :  printf("     ");
					   break;
			} 
		}
		couleur("31");
		printf("|\n");
		couleur("0");
	}
	for(x=0;x<nx+1;x++)
	{
		couleur("31");
		printf("+-----");
		if(x==nx)
		{
			printf("+\n");
		}
		couleur("0");
	}
}

/* Explication de la fonction LOAD */
/* Fonction de lecture du fichier */
/* Lecture du fichier a partir de la commande FGETS */
/* Lecture ligne par ligne */
/* En fonction du premier caractere, je peux definir le masque pour la commande SSCANF et */
/* Ainsi recupere les differentes valeurs correspondant au type de la case, on rempli la structure */
/* et on termine en fermant le fichier */

void chargement(long int nx,long int ny,grille **tab,char fichier[20])
{
	char type,temp[14];
	int ligne,colonne,valeur1,valeur2,i,j;
	int compteur=0,drap=0;
	FILE *jeu; 
	jeu=fopen(fichier,"r"); 
	if(jeu==NULL) 
	{
		printf(" \n Fichier non charge \n");
	}
	else
	{
		printf(" Fichier %s charge \n",fichier);

/* Fonction de lecture du fichier. */
/* Attrib est specifique de ce que je peux faire, */
/* 0 affecte aux cases definitions, impossible de faire une action dessus. */
/* 1 affecte aux cases noires, impossible de faire une action dessus. */
/* -1 affecte aux cases blanches(Z), possibilite d'agir dessus */
/* 3 affecte aux cases predifinies, case remplie par le joueur, impossible d'agir dessus sauf en supprimant la valeur, */
/* et reaffectant la case en case blanche(Z) voir plus bas dans supprimvaleur */

		while(!feof(jeu))
		{
			fgets(temp,14,jeu);
			if(drap==0)
			{
				tab=(grille **)malloc(nx * sizeof(grille *));
				if(tab==NULL)
				{
					printf("\n Echec lors de l'allocation memoire!!!");
				}
				printf("La memoire allouee est de %d ",sizeof(grille*));
				for(i=0;i<nx;i++)
				{
     					tab[i] = (grille *)malloc(ny * sizeof(grille));
					printf("La memoire allouee est de %d\n",sizeof(grille));
    					if (tab[i] == NULL)
     					{
    						for (j=0;j<ny;j++)
     						{
  							printf("%d",j);
							free(tab[j]);
    							printf("\n Echec lors de l'allocation memoire!!!");
     						}
     				 	}
     				}
				drap=1;
			}
		if(temp[0]=='D')
		{
			sscanf(temp,"%c %d %d %d %d",&type,&ligne,&colonne,&valeur1,&valeur2);
			tab [ligne][colonne].attrib=0; 
			tab [ligne][colonne].type=type;
			tab [ligne][colonne].val[0]=valeur1;
			tab [ligne][colonne].val[1]=valeur2;
		}
		if(temp[0]=='N')
		{
			sscanf(temp,"%c %d %d",&type,&ligne,&colonne);
			tab [ligne][colonne].type=type;
			tab [ligne][colonne].attrib=1;
		}
		if(temp[0]=='B')
		{
			tab[ligne][colonne].attrib=-1;
		}
		if(temp[0]=='P')
		{
			sscanf(temp,"%c %d %d %d",&type,&ligne,&colonne,&valeur1);
			tab [ligne][colonne].type=type;
			tab [ligne][colonne].val[0]=valeur1;
			tab [ligne][colonne].attrib=3;
		}		
		compteur++;
		}
		fclose(jeu);
	}

}

/* Explication de la fonction SAUVEGARDE */
/* Fonction de sauvegarde du jeu */
/* On ouvre un fichier en ecriture, et ecrase l'ancien s'il y en a deja un nomme comme cela */
/* et on enregistre les donnees dedans */
/* Enregistrement case par case dans un fichier texte au meme format que les grilles de depart */

void sauvegarde(int nx,int ny,grille **tab,char fichier[20])
{
	int x,y;
	FILE *savegame;
	char nom_fic[20];
	printf("Veuillez entrer le nom du fichier : ");
	fgets(nom_fic,20,savegame);
	savegame=fopen(nom_fic,"w");
	if(savegame==NULL)
	{
		printf("Fichier non sauvegarde");
	}
	else
	{
		for(x=0;x<nx;x++)
		{
			for(y=0;y<ny;y++)
			{
				if(tab[x][y].type=='D')
				{
					fprintf(savegame,"%c %d %d %d %d\n",tab[x][y].type,x,y,tab[x][y].val[0],tab[x][y].val[1]);
				}	
				if(tab[x][y].type=='B'||tab[x][y].type=='N')
				{	
					fprintf(savegame,"%c %d %d\n",tab[x][y].type,x,y);
				}
				if(tab[x][y].type=='P')
				{
					fprintf(savegame,"%c %d %d %d\n",tab[x][y].type,x,y,tab[x][y].val[0]);
				}
			}
		}
	}
}

/* Explication de la fonction VERIFREGLE*/
/* Fonction de verification pour les lignes horizontales */
/* Premierement, on cherche la valeur de la case definition correspondant a la ligne */
/* Dont la valeur fait partie, de la on sait d'ou notre verification doit partir (valeur DEPART) */
/* Ensuite, on demarre du DEPART, on teste si la valeur se trouve deja sur la ligne */
/* Si elle se trouve sur la ligne, on retourne directement 0 */
/* Si la valeur ne se trouve pas sur la ligne, on verifie que la case est soit Blanche, soit Predefinie */
/* Si elle l'est, alors on fait la somme de la valeur actuelle avec les valeurs precedentes. */
/* Si a un moment donne, la colonne est differente de DEPART et que les cases sont differentes de B ou P */
/* On a une case vide en plus de celle que l'on veut attribuer et donc cela ne sert a rien de tester la somme */
/* En fin de fonction, on verifie les deux cas possibles et on retourne 1 si c'est possible ou 0 si ca ne l'est pas */

int verifregle(int nx,int ny,grille **tab,int ligne,int colonne,int valeur)
{
	int test=0,co,somme=valeur;
	int depart,def,echec=1;
	co=colonne;
	while(test==0)
	{
		if(tab[ligne][co-1].type=='D')
		{
			def=tab[ligne][co-1].val[0];
			depart=co;
			test=1;
		}
	co--;
	}
	for(depart=depart;depart<nx&&tab[ligne][depart].type!='N'&&tab[ligne][depart].type!='D';depart++)
	{	
		if(valeur!=tab[ligne][depart].val[0])
		{
			if(tab[ligne][depart].type=='P'||tab[ligne][depart].type=='B')
			{
				somme=somme+tab[ligne][depart].val[0];
			}
			else if(colonne!=depart)
				echec=0;
		}
		else
		{
			return 0;
		}
	}
	if( (echec==1&&somme==def)||(echec==0&&somme<def))
		return 1;
	else
		return 0;
}

/* Explication de la fonctoion VERIFREGLE2 */
/* Fonction de verification pour les lignes verticales */
/* Premierement, on cherche la valeur de la case definition correspondant a la colonne */
/* Dont la valeur fait paretie, de la sait d'ou notre verification doit partir (valeur DEPART) */
/* Ensuite, on demarre du DEPART, on teste si la valeur se trouve deja sur la colonne */
/* Si elle se trouve sur la ligben on retourne directement 0 */
/* Si la valeur ne se trouve pas sur la colonne, on verifie que la case est soit Blanche, soit Predefinie */
/* Si elle l'est, alors on fait la somme de la valeur actuelle avec les valeurs precedentes. */
/* Si a un moment donne, la ligne est differente de DEPART et que les cases sont differentes de B ou de P */
/* On a une case videen plus de celle que l'on veut attribuer et donc cela ne sert a rien de tester la somme */
/* En fin de fonction, on verifie les deux cas possibles et on retourne 1 si c'est possible ou 0 si ca ne l'est pas */

int verifregle2(int nx,int ny,grille **tab,int ligne,int colonne,int valeur)
{
	int test=0;
	int somme=valeur;
	int def,echec=1,depart,li;
	li=ligne;
	while(test==0)
	{
		if(tab[li-1][colonne].type=='D')
		{
			def=tab[li-1][colonne].val[1];
			depart=li;
			test=1;
		}
	li--;
	}
	for(depart=depart;depart<ny&&tab[depart][colonne].type!='N'&&tab[depart][colonne].type!='D';depart++)
	{	
		if(valeur!=tab[depart][colonne].val[0])
		{
			if(tab[depart][colonne].type=='P'||tab[depart][colonne].type=='B')
				somme=somme+tab[depart][colonne].val[0];		
			else if(ligne!=depart)
				echec=0;
		}
		else
		{
			return 0;
		}
	}
	if((echec==1&&somme==def)||(echec==0&&somme<def))
			return 1;
	else
			return 0;
}

/* Explication de BACKTRACKING */
/* Ne fonctionne pas correctement, plante sur la grille 32,34,35 */

int backtracking(int nx,int ny,grille **tab, int lig, int colo)
{
	int i=1;
	if(colo==ny)
	{
		lig=lig+1;
		colo=0;
	}
	while(tab[lig][colo].type=='D'||tab[lig][colo].type=='N'||tab[lig][colo].type=='P') /* Test pour voir le type de ligne */
	{	
		colo++;			/* Tant que c'est un des types test, on avance dans la colonne */
		if(colo==ny)		/* Si on arrive en fin de colonne, on avance d'une ligne et on reviens a la case 0 de la colonne */
		{
			lig=lig+1;
			colo=0;
		}
	}
	if((colo==0&&lig==nx)||(colo==ny&&lig==nx))   /*Sert a verifier que je suis a la fin du tableau*/
		return 1;
	else
	{
		while(i<10)  									/* Test tant que i est plus que 10 */
												/* Si les 2 regles sont verifiees, on change le type de */
												/* en case Blanche, on affecte la valeur de i a la case */
												/* Ensuite on teste ce que la fonction et la colonne */
												/* renvoie */
												/* Sinon on reaffecte un type vide a la case ainsi que la */
												/* valeur 0 */
												/* On incremente le compteur et on recommence */ 
		{
			if(verifregle(nx,ny,tab,lig, colo,i)==1 && verifregle2(nx,ny,tab,lig, colo,i)==1)
			{
				tab[lig][colo].type='B';
				tab[lig][colo].val[0]=i;
				if(backtracking(nx,ny,tab, lig,colo+1)==1)
					return 1;
			}
			else
			{ 
				tab[lig][colo].type='Z';
				tab[lig][colo].val[0]=0;
			}
			i++;
		tab[lig][colo].type='Z';
		tab[lig][colo].val[0]=0;
		}
		return 0;
	}
}
void resolutionautomatique(int nx,int ny,grille **tab)
{
	int colo=0,lig=0;
	/*fonction qui me permet de trouver le depart */
	if( backtracking(ny,nx,tab, colo, lig)==1 )
		printf("Solution possible\n");
	else 
		printf("solution impossible\n");
}

/* Explication de la fonction JEU */
/* Introduction des coordonnees de la case par le joueur */
/* Fonction de test de valeur introduite par le joueur */
/* Test de la case pour savoir si celle ci est libre ( type Z ) ou si elle correspond a un autre type */
/* Si la case est libre, introduction de la valeur par le joueur puis test de la valeur entree avec les */
/* fonction VERIFREGLE et VERIFREGLE2 */
/* Si test ok, la valeur est encodee, sinon on refais la boucle */

void jeu(int nx,int ny,grille **tab)
{
	int ligne,colonne,valeur,test;
	do
	{
		printf("Veuillez entrer le numero de la ligne : \n");
		scanf("%d",&ligne);
		printf("Veuillez entrer le numero de la colonne : \n");
		scanf("%d",&colonne);
		if(tab[ligne][colonne].attrib==0)
		{
			test=0;
			affichage(nx,ny,tab);
			couleur("1;31");
			printf("Ceci est une case definition \n");
			couleur("0");
		}
		if(tab[ligne][colonne].attrib==1)
		{
			test=0;
			affichage(nx,ny,tab);
			couleur("1;31");
			printf("Ceci est une case noire \n");
			couleur("0");
		}
		if(tab[ligne][colonne].attrib==3)
		{
			test=0;
			affichage(nx,ny,tab);
			couleur("1;31");
			printf("Cette case a deja ete remplie par le joueur \n");
			couleur("0");
			
		}
		if(tab[ligne][colonne].attrib==-1)
		{
			printf("Veuillez introduire votre valeur comprise entre 1 et 9 inclus : \n");
			scanf("%d",&valeur);
			if(valeur>0&&valeur<10)
			{
				if(verifregle(nx,ny,tab,ligne,colonne,valeur)==0||verifregle2(nx,ny,tab,ligne,colonne,valeur)==0)
				{
					affichage(nx,ny,tab);
					couleur("1;31");
					printf("Cette valeur se trouve deja sur la ligne et/ou la colonne, ou la somme n'est pas correcte \n");
					couleur("0");
					test=0;
				}
				else
				{
					tab[ligne][colonne].type='P';
					tab[ligne][colonne].val[0]=valeur;
					tab[ligne][colonne].attrib=3;
					test=1;
				}
			}
			else
			{
				couleur("1;31");
				printf("La valeur introduire n'est pas comprise dans la fourchette\n");
				printf("Veuillez recommencer\n");
				couleur("0");
			}
		}	
	}
	while(test==0);
}

/* Explication de la fonction SUPPRIMVALEUR */
/* Fonction pour supprimer la valeur d'une case */
/* Meme test que pour la fonction JEU, pour ne pas effacer n'importe quoi */
/* Si case ok, la valeur est supprimee et on repasse la case en type Z pour pouvoir reecrire dedans par la suite */
/* J'etais d'abord passe par une fonction pour modifier la valeur plutot que de la supprimer, mais cela pose */
/* des problemes au niveau des regles de verification */

void supprimvaleur(int nx,int ny,grille **tab)
{
	int ligne,colonne,valeur,test;
	do
	{
		printf("Veuillez entrer le numero de la ligne : \n");
		scanf("%d",&ligne);
		printf("Veuillez entrer le numero de la colonne : \n");
		scanf("%d",&colonne);
		if(tab[ligne][colonne].attrib==-1)
		{
			affichage(nx,ny,tab);
			printf("Il n'y a rien a supprimer ici ici \n" );
			test=0;
		}
		if(tab[ligne][colonne].attrib==0)
		{
			affichage(nx,ny,tab);
			printf("Ceci est une case definition \n");
			test=0;
		}
		if(tab[ligne][colonne].attrib==1)
		{
			affichage(nx,ny,tab);
			printf("Ceci est une case noire \n");
			test=0;
		}
		if(tab[ligne][colonne].attrib==3)
		{
			tab[ligne][colonne].type='Z';
			tab[ligne][colonne].val[0]=valeur;
			tab[ligne][colonne].attrib=-1;
			printf("La valeur a ete supprimee \n");
			test=1;
		}
	}
	while(test==0);

}

/* Explication de la fonction AIDE */
/* Fonction pour l'aide sur les valeurs possibles que peut prendre une case */
/* Introduction par le joueur des coordonnees de la case */
/* Test des valeurs de 1 a 9 par les fonctions VERIFREGLE et VERIFREGLE2 */
/* Les valeurs retournees sont celles qui sont possibles d'introduire  */

void aide(int nx,int ny,grille **tab, int ligne, int colonne)
{
	int i;
	printf("Solution possible : ");
	for(i=1;i<10;i++)
	{
		if(verifregle(nx,ny,tab,ligne,colonne,i)==1&&verifregle2(nx,ny,tab,ligne,colonne,i)==1)
		{
			printf("%d est une valeur possible pour cette case \n", i);
		}
	}
	printf("\n");
}

/* Explication de la fonction MENU2 */
/* Menu de JEU avec la grille */
/* 1. Entrer une valeur, permet d'entrer une valeur en appelant la fonction JEU */
/* 2. Supprimer une valeur, supprimer la valeur d'une case en appelant la fonction SUPPRIMVALEUR */
/* 3. Demander de l'aide, introduction des coordonnees par le joueur puis appel de la fonction AIDE */
/* 4. Resolution automatique, appel de la fonction resolutionautomatique ( BACKTRACKING ) */
/* 5. Sauvegarde de la partie, enregistre la partie dans un fichier txt en appelant la fonction SAUVEGARDE */
/* 6. Retour, retour au menu principal  */

void menu2(int nx,int ny,grille **tab)
{
	int ligne,colonne;
	char choix2;
	char fichier[20]="";
	do
	{
		affichage(nx,ny,tab);
		couleur("1;34");
		printf("***** MENU DU JEU *****\n");
		couleur("0");
		couleur("31");		
               	printf("-----------------------\n");
		couleur("0");
		printf("Que voulez-vous faire ?\n"
                       "1.Entrer une valeur\n"
		       "2.Supprimer une valeur\n"	
                       "3.Demander de l'aide\n"
                       "4.Resolution automatique\n"
	  	       "5.Sauvegarde de la partie\n"
                       "6.Retour\n");
		do
			choix2=getchar();
		while((int)choix2<49||(int)choix2>55);
		switch ((int)choix2)
		{
			case 49 : jeu(nx,ny,tab);
			          break;
			case 50 : supprimvaleur(nx,ny,tab);
			          break;
			case 51 : printf("Entrez le numero de la ligne : \n");
			          scanf("%d",&ligne);
				  printf("Entrez le numero de la colonne : \n");
				  scanf("%d",&colonne);
				  aide(nx,ny,tab,ligne,colonne);
			          break;
			case 52 : resolutionautomatique(nx,ny,tab);
				  break;
			case 53 : sauvegarde(nx,ny,tab,fichier);			 			 
				  break;
		}
	}
	while((int)choix2!=54);
}

/* Explication de la fonction MENU3 */
/* Menu de selection de difficulte de la grille */
/* 3 difficultes sont proposees */ 
/* Utilisation de la commande SPRINTF pour concatener les valeurs introduites par le joueur */

void menu3(int nx,int ny,grille **tab)
{
	int choix3,choix4;
	char fichier[20]="";
	do
	{	
		couleur("1;34");
		printf("Selectionnez le niveau de difficulte\n");
		couleur("0");
		printf("-------------1.Facile---------------\n"
		       "-------------2.Moyen----------------\n"
		       "-------------3.Expert---------------\n"
		       "-------------4.Retour---------------\n");
		scanf("%d",&choix3);
	}
	while(choix3<1 || choix3>4);
	if((int)choix3!=4) 
	{
		do
		{
			printf("Selectionnez votre grille de 1 a 5 : \n");
			scanf("%d",&choix4);
		}
		while(choix4<1 || choix4>5);
			sprintf(fichier,"grille%d%d.txt",choix3,choix4);
			chargement(nx,ny,tab,fichier);
			menu2(nx,ny,tab);
	}
}

/* Explication de la fonction MAIN */
/* Menu de depart  */

int main()
{
	long int nx=9;
	long int ny=9;
	grille **tab;
	char choix,fichier [20];
	printf("\033[H\033[2J");
	couleur("1;34");
	printf( "*****************************************************************************************************************\n"
		"*****************************************************************************************************************\n"
		"**													       **\n"
		"*														*\n"
		"*														*\n"
		"*														*\n"
		"*														*\n"
		"*														*\n"
		"*					BIENVENUE DANS LE JEU KAKURO						*\n"
		"*														*\n"
		"*														*\n"
		"*														*\n"
		"**													       **\n"
		"****************************************************************************************************************\n"
		"****************************************************************************************************************\n");
		printf("\n");
		printf("  Tapez 1 pour Jouer\n "
		       " Tapez 2 pour charger une partie sauvegardee\n "
                       " Tapez 3 pour quitter le jeu\n");
		do
		{
			choix=getchar();
		while((int)choix<49 || (int)choix>52);
		switch((int)choix)
		{	
			case 49 : menu3(nx,ny,tab);
				  break;
			case 50 : printf("Entrer le nom du fichier en specifiant son extension (.TXT) : \n");
				  scanf("%s",fichier);
				  chargement(nx,ny,tab,fichier);
				  menu2(nx,ny,tab);
				  break;
		}
	}
	while((int)choix!=51);
	return 0; 
}

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.