Uwzględnianie zwrotów nie ma sensu. Interesują nas tylko kierunki, które mamy dwa. To czy jakiś statek ustawi się od lewej, czy od prawej nie ma najmniejszego znaczenia!
Poza tym, aby oderwać się na chwilę od tego, co robię aktualnie, napisałem taki kod, korzystając z Twojego @Niko0 pomysłu
poszczególne komórki wypełniać jedynkami - tam gdzie jest statek, ale w tej grze statki nie mogą się stykać burtami, więc statek oznaczyłbym jako 2, a jedynkami otoczył każdy z okrętów.
Sam na początku myślałem o używaniu po prostu booli i sprawdzaniu czy kolejny okręt nie koliduje. ale w przypadku robienia graficznie mapy statki mogą być otoczone inna grafiką, więc przychyliłem się do tej wersji. Kod wymaga poprawek, ale niewiele da się zrobić w piętnastominutową przerwę.
#include <cstdio>
#include <cstdlib>
#include <ctime>
using namespace std;
class plansza //jednego gracza
{
static const unsigned int liczba_statkow,statki[],bok;
short unsigned int** const pola;
void uzupelnij(void); //uzupelnianie planszy statkami
bool dodaj_statek(unsigned int); //dlugosc statku
public:
plansza(void);
void narysuj(void);
bool sprawdz_trafienie(unsigned int,unsigned int);
~plansza(void);
};
int main(void)
{
srand(time(NULL));
plansza komputera;
komputera.narysuj();
return 0;
}
const unsigned int plansza::liczba_statkow = 5,plansza::statki[liczba_statkow] = {5,4,3,3,2},plansza::bok = 10;
plansza::plansza(void) : pola(new short unsigned int*[bok])
{
for(unsigned int i = 0;i<bok;++i)
pola[i]=new short unsigned int[bok];
for(unsigned int i = 0;i<bok;++i)
for(unsigned int j=0;j<bok;++j)
pola[i][j]=0;
uzupelnij();
}
void plansza::uzupelnij(void)
{
//tutaj mozna dodac opcje recznego uzupelniania
for(unsigned int i=0;i<liczba_statkow;++i)
while(!dodaj_statek(statki[i]));
}
bool plansza::dodaj_statek(unsigned int dlugosc_statku)
{
bool kierunek = rand()%2; // 1 - pionowy, 0 - poziomy
unsigned int x = rand() % (( kierunek ? bok : (1+bok-dlugosc_statku))),y=rand() % ( kierunek ?(1+bok-dlugosc_statku) : bok);
for(unsigned int i=0;i<dlugosc_statku;++i)
if(pola[x+(kierunek?0:i)][y+(kierunek?i:0)]>0)
return false;
if(kierunek?y:x!=0)
pola[x-(!kierunek)][y-kierunek]=1;
if((kierunek?y:x) + dlugosc_statku < bok)
pola[x+(kierunek?0:dlugosc_statku)][y+(kierunek?dlugosc_statku:0)]=1;
for(unsigned int i=0;i<dlugosc_statku;++i)
{
pola[x+(kierunek?0:i)][y+(kierunek?i:0)] = 2;
if(kierunek?x:y > 0)
pola[x-(kierunek)+(kierunek?0:i)][y-(!kierunek)+(kierunek?i:0)] = 1;
if((kierunek?x:y)+1<bok)
pola[x+(kierunek)+(kierunek?0:i)][y+(!kierunek)+(kierunek?i:0)] = 1;
}
return true;
}
void plansza::narysuj()
{
for(unsigned int i = 0;i<bok;++i,printf("\n"))
for(unsigned int j=0;j<bok;++j)
printf("%c ",(pola[i][j])==2?'X':'-');
}
bool plansza::sprawdz_trafienie(unsigned int x,unsigned int y)
{
return (pola[x][y]==2);
}
plansza::~plansza(void)
{
for(unsigned int i = bok;i--;)
delete[] pola[i];
delete[] pola;
}
Jak widać nie skupiałem się tutaj na wykorzystaniu za wszelką cenę wylosowanego punktu. Nie ma to większego sensu, każda pozycja (dopuszczalna przez grę) jest dobra. Jeśli nie trafiłem, losowałem ponownie, a nie przekształcałem to, co już miałem.
Teraz zostaje najtrudniejsza część, czyli inteligentne strzelanie przez komputer :). Chodzi o to, żeby po trafionym strzale dalsze trafienia szły jeden po drugim, w danym kierunku. Jeśli ktoś ma jakieś sugestie to z chęcią wysłucham :)
Nie jest to wcale takie trudne. Ogólnie to robisz planszę graczowi (komputerowi), na której zaznacza gdzie już strzelił. Na początku te strzały odbywają się w pełni losowo, ale jeśli trafią na już użyte pole, jest losowane kolejne (komputer nie zgłasza, że chce strzelać ponownie w użyte miejsce). Dodatkowo, gdy już trafisz jakiś statek to sprawdzasz (kolejnymi strzałami), w którym kierunku jest on ustawiony i strzelasz, aż go zatopisz. Możesz sprawdzać, czy zakończyłeś zadanie poprzez upewnienie się, że strzały na jedną i na drugą stronę trafiają w wodę, ale lepszym rozwiązaniem (jeśli to możliwe) jest upewnianie się na podstawie statków będących w grze. Jeśli najdłuższy grający aktualnie statek przeciwnika ma długość 4 i Ty właśnie trafiłeś 4. sektor, wiesz, że cały okręt zatonął. Ponadto, jeżeli statek zetknie się z polem, na którym statku nie ma (wiesz to już, np. strzeliłeś tam) to też nie ma sensu ciągnąć w tą stronę tego okrętu. Dodatkowo należy pamiętać, że na około statków (przed, za i obok, w sumie 2(n+1) pól wody) nie może być innych statków, więc i tam nie należy oddawać strzałów (jak już zestrzelisz statek to otaczasz go polami, w które też nie będziesz strzelał). Może jak znajdę chwilę to i to napiszę.
Komunikację "komputera z planszą" przeprowadzałbym tylko przez taką funkcję jak w moim kodzie sprawdz_trafienie.
PS
Oczywiście przeciwnik nie powinien mieć możliwości wyświetlania sobie planszy (nieswojej) ;). Po fazie testów ta opcja powinna zniknąć.