Obsługa błedów

0

Cześć, mam problem z obsługą błędów w programie, gdzie użytkownik może sterować skoczkiem szachowym, aby rozwiązać tzw problem skoczka. Poza częścią sprawdzającą poprawność danych, algorytm działa poprawnie. Największy problem sprawia mi fragment, gdzie użytkownik wpisuje, na jakim polu skoczek powinien się pojawić w następnym ruchu. Mam tu na myśli zabezpieczenie przeciwko dowolnej liczbie znaków (nieważne czy są to cyfry czy litery). Ponadto, w przypadku błędnego ruchu (z powodu wpisania złej cyfry), program powinien wracać do poprzedniego poprawnie wykonanego punktu. Domyślam się, że problem można rozwiązać poprzez funkcję getchar, co też próbowałem, ale bez skutku. Proszę o pomoc w tej kwestii. Poniżej zamieszczam kod programu:

#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <ctype.h>
#define N 8
#define NP N+1

int wyjscie(int, int, int[N][NP]);
void wyswietl(int[N][NP]);
void wypelnij(int [N][NP]);
int znajdz(int, int, int, int[N][NP], int[N], int[N]);
void nast_ruch(int, int, int, int [N], int [N], int, char);

int main()
{
	char ch = '\0';
	do
	{
		printf("1. Nowa gra.\n");
		printf("2. Zamknij program\n");
		printf(" \nWybierz opcje:\n");
		scanf("%c", &ch);
		switch (ch)
		{
		case '1': 
			skoczek();
		case '2':
			break;
		default:
			printf("Taka opcja nie istnieje!\n");
		}
	} while (ch != '2');
	system("pause");
	return 0;
}

void wypelnij(int tab[N][NP])
{
	int i, j, k;
	for (i = 0, k = N; i < N; i++, k--)
	{
		tab[i][0] = k; /* pionowe oznaczenie szachownicy */
		for (j = 1; j < NP; j++)
		{
			tab[i][j] = 0; /* wypelnianie szachownicy cyfra 0 poza jej pierwszym wierszem i pierwsza kolumna */
		}
	}
}

int skoczek()
{
	int tab[N][NP];
	int i, j, k, a, b, m, n;
	char s, c;
	
	/* tablice zawierajace 8 mozliwych ruchow skoczka, osobno dla kierunku pionowego i poziomego */
	int x_ruch[] = { 2, 1, -1, -2, -2, -1, 1, 2 };
	int y_ruch[] = { 1, 2, 2, 1, -1, -2, -2, -1 };
	wypelnij(tab);
	wyswietl(tab);
	printf("\nPodaj pole poczatkowe dla konika szachowego: \n");
	scanf("%c%c", &s, &c);
	printf("\n");
	m = c - '0'; /* zamiana z char na int */
	if (
		(s >= 'a') && 
		(s <= 'h')
		) /* zamiana na duza litere, jesli wprowadzona zostala mala */
	{
		s = toupper(s);
	}
	
	if (s >= 'A' && s <= 'H' && m > 0 && m <= N)
	{
		/* zamienienie wartosci poczatkowej na odpowiedni indeks tablicy */
		for (i = 0, j = N; i < N && j > 0; i++, j--)
		{
			if (m == i)
			{
				a = j;
			}
			if (s == 'A' + i)
			{
				b = i + 1;
			}
		}/* for */
		tab[a][b] = 1; /* zainicjalizowanie pola poczatkowego wartoscia 1 */
		/* szukanie rozwiazania problemu */
		if (znajdz(a, b, 2, tab, x_ruch, y_ruch) == 0)
		{
			printf("\nBlad danych.\nNie znaleziono rozwiazania!\n");
			return 0;
		}
		else
		{
			printf("Znaleziono rozwiazanie!\n");
			wyswietl(tab);
		}
	}
	else
	{
		printf("Nie wprowadzono prawidlowych danych.\n");
	}
}
/*Sprawdza, czy skoczek nie wyjdzie poza szachownice w nastepnym ruchu*/
int wyjscie(int x, int y, int tab[N][NP])
{
	if (
		(x >= 0) && 
		(x < N) &&
		(y >= 1) &&
		(y < NP) &&
		(tab[x][y] == 0)
		)
	{
		return 1;
	}
	else return 0;
}


void wyswietl(int tab[N][NP])
{
	char ozn[] = { ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; /* poziome oznaczenie szachownicy */
	int i, j, k;
	for (k = 0; k < NP; k++)
	{
		printf("%2c ", ozn[k]); /* wyswietlanie poziomego oznaczenia */
	}
	printf("\n");
	for (i = 0; i < N; i++)
	{
		for (j = 0; j < NP; j++)
		{
			printf("%2d ", tab[i][j]); /* wyswietlanie szachownicy */
		}
		printf("\n");
	}
}

/* Wyswietlenie mozliwych ruchow skoczka w nastepnym kroku */
void nast_ruch(int x, int y, int tab[N][NP], int x_ruch[N], int y_ruch[N], int wx, char wy)
{
	int ax, ay, k;
	for (k = 0; k < N; k++)
	{
		ax = x + x_ruch[k];
		ay = y + y_ruch[k];
		if (wyjscie(ax, ay, tab) == 1)
		{
			printf("Mozliwy ruch na %c%d, nacisnij %d\n", wy + y_ruch[k], wx - x_ruch[k], k + 1);
		}
	}
}

/* Funckja przedstawiajaca rekurencyjny algorytm poruszania sie skoczka */
int znajdz(int x, int y, int ruch, int tab[N][NP], int x_ruch[N], int y_ruch[N])
{
	int ax, ay, i, j, k, m, w;
	char s,c, znak = '\0';
	if (ruch == (N*N)+1) /* sprawdzenie, czy dany ruch nie jest ostatnim, tzn. czy znaleziono roziazanie */
	{
		return 1;
	}
	/* zamiana danego indeksu tablicy na wartosc zgodna z oznaczeniem szachownicy */
	for (i = 0, j = N; i < N && j > 0; i++, j--)
	{
		if (x == i)
		{
			m = j;
		}
		if (y == i + 1)
		{
			s = 'A' + i;
		}
	}
	wyswietl(tab);
	nast_ruch(x, y, tab, x_ruch, y_ruch, m, s);
	printf("Podaj nastepny krok ruchu konika:\n");
	if (((scanf("%c", &znak)) != 1) && (znak < '0' || znak > '8'))
	{
		printf("Blad danych, wpisz ponownie\n");
		scanf("%c", &znak);
	}
	w = znak - '0';
	for (k = 0; k <= w; k++)
	{
		if (k == w)
		{	/* zamiana wartosci zgodnej z oznaczeniem tablicy na indeks tablicy */
			for (i = 0, j = N; i < N && j >0; i++, j--)
			{
				if (m == i)
				{
					x = j;
				}
				if (s == 'A' + i)
				{
					y = i + 1;
				}
			}
			ax = x + x_ruch[w - 1];
			ay = y + y_ruch[w - 1];
		}
	}
	if (wyjscie(ax, ay, tab) == 1) /* sprawdzenie, czy w danym ruchu skoczek nie wyskoczy poza szachownice */
	{
		tab[ax][ay] = ruch; /* numerowanie pol, w ktorych znajduje sie skoczek, zgodnie z iloscia wykonanych ruchow */
		if (znajdz(ax, ay, ruch + 1, tab, x_ruch, y_ruch) == 1) /* sprawdzenie, nastepny ruch skoczka jest mozliwy */
		{
			return 1;
		}
		else
		{
			tab[ax][ay] = 0;
		}
	}
	return 0;
}
3
  1. Podziel to na mniejsze funkcje, najlepiej takie które mają w nazwie czynności.
  2. Oddziel obliczenia od wypisywania i wprowadzania wartości
  3. Zrób funkcje tak małe, żeby była w nich co najwyżej jedna pętla a zmienne ustawiane tylko w jednym miejscu (poza inicjalizacją)
  4. zamiast:
/*Wyswietla szachownice*/
void wyswietl(int tab[N][N + 1])

napisz:

void wyswietl_szachownice(int tab[N][NP1])

gdzie NP1 = N + 1.

W tym wypadku komentarz sygnalizuje "zapach" i oznacza że nazwa jest za mało szczegółowa.

  1. to działa, chociaż jest absurdalne:
    if (s >= 'a' && s <= 'h') /* zamiana na duza litere, jesli wprowadzona zostala mala */
    {
        s = s - ' ';
    }

Powinno być:

s = toupper(s);

lub ostatecznie:

    if (s >= 'a' && s <= 'h') /* zamiana na duza litere, jesli wprowadzona zostala mala */
    {
        s = (s - 'a') + 'A';
    }
  1. nie używaj zmiennych o nazwie "l" - łatwo je pomylić z "1".

        for (j = 1; j < N + 1; j++)
        {
            tab[i][j] = 0; /* wypelnianie calej szachownicy cyfra 0 */
        }

Nie całej.

  1. brakuje kontroli rozmiaru tablicy ozn.

Długość tablicy możesz obliczyć tak:

#include <assert.h>
char ozn[] = { ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' };
unsigned int ozn_size = sizeof(ozn) / sizeof(char);

A sprawdzić tak:

assert(ozn_size == N + 1);
  1. stosuj nawiasy i łamanie linii w złożonych warunkach if

zamiast:

if (x >= 0 && x < N  && y >= 1 && y < N + 1 && tab[x][y] == 0)

napisz:

if ( 
    (x >= 0)
    && 
    (x < N)
    && 
    (y >= 1)
    && 
    (y < N + 1) 
    && 
    (tab[x][y] == 0)
  )
{
   // cos tam
}
  1. A jeśli chodzi o to co użytkownik wprowadza - niech wprowadza znaki zamiast liczb.

Zamiast:

int x = 0;
scanf("%d", &x); 

Daj:

char ch = '\0';
scanf("%c", &ch); 

i popraw switch.

0

Trochę udało mi się poprawić, jednak pojawiły się inne problemy. Co zrobić, kiedy po wpisaniu więcej niż jednego znaku przy wyborze ze swicha, by wyświetlał się komunikat o błędzie tylko raz, a nie tyle razy, ile jest znaków? Podobnie sytuacje wygląda przy pobieraniu wartości następnego ruchu funkcją scanf w funkcji skoczek

1

Możesz zamienić na wczytywanie całej linii tekstu:

http://stackoverflow.com/questions/17931814/skip-remainder-of-line-with-fscanf-in-c

0

Dzięki, teraz jest już w miarę dobrze. Nie wiem, jak mogę w instrukcji zdefiniować: czy następny znak jest pusty, w sensie że napis składa się np. z jednej cyfry? Chodzi o to, że jeśli wpisze 11 bo wykona się instrukcja zdefiniowana dla 1

0
kolkol99 napisał(a):

Dzięki, teraz jest już w miarę dobrze. Nie wiem, jak mogę w instrukcji zdefiniować: czy następny znak jest pusty, w sensie że napis składa się np. z jednej cyfry? Chodzi o to, że jeśli wpisze 11 bo wykona się instrukcja zdefiniowana dla 1

Sprawdzaj długość linii albo drugi znak, jeśli komendy mogą być tylko jedno-znakowe - drugi znak w linii powinien być równy '\0'.

0

Przed if-ami daj:

char cmd = '\0';

if (
    (t[0] != '\0')
    && 
    (t[0] == '\0')
  )
{
  cmd = t[0];
}

A potem:

if (cmd == '1') {
    skoczek();
} else if (cmd != '2') {
    printf("\nTaka opcja nie istnieje\nSprobuj ponownie\n");
}

} while (cmd != '2'); 
0

Dzięki za pomoc :) kwestie znaków rozwiązałem jednak funkcją strlen, nawet nie wiedziałem że będzie ona w takim przypadku działała. Jak się okazało, krokiem przełomowym było wczytywanie całych napisów, zamiast znaków. Później już jakoś to dalej poszło.
Program działa aż miło, problem rozwiązany :D

1 użytkowników online, w tym zalogowanych: 0, gości: 1