Random walk in a 10X10 array - c

Write a C program that generates a random walk across a 10x10 array. Initially, the array will contain only dot characters. The program must randomly “walk” from element to element, always going up, down, left, or right by one step. The elements visited by the program will be labeled with the letters A through Z, in the order visited.
It doesn't take the walk to an element that has already a letter assigned (blocked element).If all four directions are blocked, the program must terminate.
I wrote the code for the above question but sometimes the output is just blank, it is just showing a black screen.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
char visited = 'A';
char a[10][10];
// assigning value '.' to array elements
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
a[i][j] = '.';
// the initial is set for the player.
int playeri, playerj;
srand(time(0));
// assigning numbers between 0-9
playeri = rand() % 10; playerj = rand() % 10;
a[playeri][playerj] = visited++;
int move;
// now to move the player
while (visited <= 'Z') {
// to generate numbers between 1-4
move = rand() % 4 + 1;
// to move up
if (move == 1) {
if (a[playeri - 1][playerj] == '.' && playeri != 0) {
playeri = playeri - 1;
a[playeri][playerj] = visited++;
}
}
// to move down
else if (move == 2) {
if (a[playeri + 1][playerj] == '.' && playeri != 9) {
playeri = playeri + 1;
a[playeri][playerj] = visited++;
}
}
// to move right
else if (move == 3) {
if (a[playeri][playerj + 1] == '.' && playerj != 9) {
playerj = playerj + 1;
a[playeri][playerj] = visited++;
}
}
// to move left
else if (move == 4) {
if (a[playeri][playerj - 1] == '.' && playerj != 0) {
playerj = playerj - 1;
a[playeri][playerj] = visited++;
}
}
}
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
printf("%c", a[i][j]);
}
printf("\n");
}
}
My guess is that the program is stuck in an infinte loop, if so, how do I solve this problem?

Your program has undefined behavior if you access array a out of bounds. This can happen when the random direction is impossible because you reached the borders of the 10x10 array. To avoid this change the order of the conditions to check the index first, e.g.
if (playeri != 0 && a[playeri - 1][playerj] == '.') {
In some cases you probably end up in a position that has no adjacent positions with a dot, so there is no way to continue. As visited does not get incremented in this case, your loop will not terminate.
An additional check that there is at least one direction not blocked will fix the endless loop, but is not an optimal solution.
Your implementation that generates a random direction and then checks if this direction is possible may need several attempts when more fields get blocked.
Although it is very unlikely you might even get random numbers that denote blocked ways for a long time.
To implement the requirement to terminate the program when all directions are blocked and to improve the behavior when many directions are blocked, I suggest to change the algorithm.
Proposed algorithm:
check all 4 directions if it is possible to walk, put all possible directions into an array of at most 4 elements and count the possible directions as n. (Example: if up, down and left are possible, the array will contain up, down, left, (invalid). The count will be n = 3.)
if n == 0 (all blocked) terminate loop
get a random number from 0 to n - 1 (Example: 0..2)
select the direction from the array (Example: random number 1 will select down)
move in the selected direction (it has been checked before that it is possible)

Related

C 100 Prisoners riddle, something wrong with my code

(If you already know what the riddle is about just read the last 2 lines)
I saw a video about a riddle which is called "The 100 prisoners riddle" it essentially tells you that a bunch of prisoners (only one person at a time) get into a room, this room has boxes that are ordered correctly from 1 to a 100 but the numbers inside the boxes are random and each prisoner getting into the room is numbered from 1 to a 100 too, so each prisoner has to pick the box that has his number, each prisoner has a set of tries (50 tries) if he opened 50 boxes and he didn't find his number he loses! for example prisoner number 1 gets in the room and he has to find the box that has his number .. it might be box number 7 or 19 or 27 who knows! so it's just a game of luck .. or is it? the game has strategies and ways to mathematically solve the puzzle but that's not my problem here, I just wanna program the game in C and solve the puzzle for myself, the code has a lot of holes in it so look closely into it and find what's the problem, THANK YOU ALL :)!
#include <stdio.h>
#include <stdlib.h>
int main() {
int i, j = 0, k = 0, counter = 0;
int boxes[10];
int boxEntered;
for (i = 0; i <= 10; i++) \\ numbering the array
boxes[i] = i;
for (i = 0; i <= 10; i++) {
int temp = boxes[i];
int randomIndex = (rand() % 10); \\ shuffling the boxes to put random numbers
boxes[i] = boxes[randomIndex];
boxes[randomIndex] = temp;
}
for (i = 0; i <= 10; i++) {
printf("%d : (%d)\n", boxes[i], i); \\ print the boxes randomized and their index ordered
}
printf("You only have 5 tries!\n");
while (k != 5) {
while (j < 10) {
printf("Pick a box number between 0 and 10 (You are number %d)\n",counter);
scanf("%d",&boxEntered);
if (boxes[boxEntered] == boxes[counter]) {
printf("\nYou succeded, PROCEED TO NEXT PRISONER\n");
j++; \\ go to the next iteration
k = 0; \\ set tries back to 0
counter++;
} else
printf("Try again\nThe box you entered had number %d\n",boxes[boxEntered]);
k++;
if (k == 5) { \\ if player prisoner fails 5 times you break the loop
break;
}
}
}
if (counter == 10) { \\ if last prisoner was reached successfully then game is won
printf("You are freed!");
} else {
printf("You are going back heheheheheh!\n")
}
return 0;
}
As you can see in this picture the output doesn't make any sense at all and i have no idea what is wrong here..
From your code's logic, you should replace
boxes[boxEntered] == boxes[counter]
with
boxes[boxEntered] == counter
This is because counter here seems to represent a prisoner. Taking boxes[counter] will give you a box, which isn't what you want; you're trying to see if the box matches the current prisoner.
Another important note is the following code will go out of bounds for your array, causing undefined behaviour:
for (i = 0; i <= 10; i++) boxes[i] = i;
boxes is declared as having size 10, and therefore taking boxes[10] goes out of bounds; the maximum is boxes[9].
To fix this, you can index your arrays starting from 1. To do this in C, instead of declaring boxes[10], use boxes[11]. This will ensure you can access boxes[10].
You can then change your loops to start from 1, so something like:
for (i = 1; i <= 10; i++) boxes[i] = i;
Be sure to make this change for every array and for loop in your code.

How to reduce time complexity in traversing a string?

I was solving a problem to find number of such indexes, a, b, c, d in a string s, of size n made only of lowercase letters such that:
1 <= a < b < c < d <= n
and
s[a] == s[c] and s[b] == s[d]
The code I wrote traverses the string character by character in a basic manner:
#include<stdio.h>
int main()
{
int n, count = 0;
char s[2002];
scanf("%d%s", &n, s);
for(int a = 0; a<n-3; a++)
{
for(int b = a + 1; b<n-2; b++)
{
for(int c = b + 1; c<n-1; c++)
{
for(int d = c + 1; d<n; d++)
{
if(s[a] == s[c] && s[b] == s[d] && a>=0 && b>a && c>b && d>c && d<n)
{
count++;
}
}
}
}
}
printf("%d", count);
return 0;
}
a, b, c and d are the indices.
The trouble is that if the input string is big in size, the time limit is exceeded due to the 4 nested loops. Is there any way I can improve the code to decrease the complexity?
The problem statement is available here: https://www.hackerearth.com/practice/algorithms/searching/linear-search/practice-problems/algorithm/holiday-season-ab957deb/
The problem can be solved if you maintain an array which stores the cumulative frequency (the total of a frequency and all frequencies so far in a frequency distribution) of each character in the input string. Since the string will only consist of lower case characters, hence the array size will be [26][N+1].
For example:
index - 1 2 3 4 5
string - a b a b a
cumulativeFrequency array:
0 1 2 3 4 5
a 0 1 1 2 2 3
b 0 0 1 1 2 2
I have made the array by taking the index of first character of the input string as 1. Doing so will help us in solving the problem later. For now, just ignore column 0 and assume that the string starts from index 1 and not 0.
Useful facts
Using cumulative frequency array we can easily check if a character is present at any index i:
if cumulativeFrequency[i]-cumulativeFrequency[i-1] > 0
number of times a character is present from range i to j (excluding both i and j):
frequency between i and j = cumulativeFrequency[j-1] - cumulativeFrequency[i]
Algorithm
1: for each character from a-z:
2: Locate index a and c such that charAt[a] == charAt[c]
3: for each pair (a, c):
4: for character from a-z:
5: b = frequency of character between a and c
6: d = frequency of character after c
7: count += b*d
Time complexity
Line 1-2:
The outer most loop will run for 26 times. We need to locate all the
pair(a, c), to do that we require a time complexity of O(n^2).
Line 3-4:
For each pair, we again run a loop 26 times to check how many times each character is present between a and c and after c.
Line 5-7:
Using cumulative frequency array, for each character we can easily calculate how many times it appears between a and c and after c in O(1).
Hence, overall complexity is O(26*n^2*26) = O(n^2).
Code
I code in Java. I do not have a code in C. I have used simple loops an array so it should be easy to understand.
//Input N and string
//Do not pay attention to the next two lines since they are basically taking
//input using Java input streams
int N = Integer.parseInt(bufferedReader.readLine().trim());
String str = bufferedReader.readLine().trim();
//Construct an array to store cumulative frequency of each character in the string
int[][] cumulativeFrequency = new int[26][N+1];
//Fill the cumulative frequency array
for (int i = 0;i < str.length();i++)
{
//character an index i
char ch = str.charAt(i);
//Fill the cumulative frequency array for each character
for (int j = 0;j < 26;j++)
{
cumulativeFrequency[j][i+1] += cumulativeFrequency[j][i];
if (ch-97 == j) cumulativeFrequency[j][i+1]++;
}
}
int a, b, c, d;
long count = 0;
//Follow the steps of the algorithm here
for (int i = 0;i < 26;i++)
{
for (int j = 1; j <= N - 2; j++)
{
//Check if character at i is present at index j
a = cumulativeFrequency[i][j] - cumulativeFrequency[i][j - 1];
if (a > 0)
{
//Check if character at i is present at index k
for (int k = j + 2; k <= N; k++)
{
c = cumulativeFrequency[i][k] - cumulativeFrequency[i][k - 1];
if (c > 0)
{
//For each character, find b*d
for (int l = 0; l < 26; l++)
{
//For each character calculate b and d
b = cumulativeFrequency[l][k-1] - cumulativeFrequency[l][j];
d = cumulativeFrequency[l][N] - cumulativeFrequency[l][k];
count += b * d;
}
}
}
}
}
}
System.out.println(count);
I hope I have helped you. The code I provided will not give time complexity error and it will work for all test cases. Do comment if you do not understand anything in my explanation.
Performing the equality check in early stages can save you some time.
Also the check a>=0 && b>a && c>b && d>c && d<n seems to be unnecessary as you are already checking for this condition in the loops. An improved version can be as follows:
#include<stdio.h>
int main()
{
int n, count = 0;
char s[2002];
scanf("%d%s", &n, s);
for(int a = 0; a<n-3; a++)
{
for(int b = a + 1; b<n-2; b++)
{
for(int c = b + 1; c<n-1; c++)
{
if(s[a] == s[c]) {
for(int d = c + 1; d<n; d++)
{
if(s[b] == s[d])
{
count++;
}
}
}
}
}
}
printf("%d", count);
return 0;
}
Since the string S is made of only lowercase letters, you can maintain a 26x26 table (actually 25x25, ignore when i=j) that holds the appearance of all possible distinct two letter cases (e.g. ab, ac, bc, etc).
The following code tracks the completeness of each answer candidate(abab, acac, bcbc, etc) by two functions: checking for the AC position and checking for the BD position. Once the value reaches 4, it means that the candidate is a valid answer.
#include <stdio.h>
int digitsAC(int a)
{
if(a % 2 == 0)
return a + 1;
return a;
}
int digitsBD(int b)
{
if(b % 2 == 1)
return b + 1;
return b;
}
int main()
{
int n, count = 0;
char s[2002];
int appearance2x2[26][26] = {0};
scanf("%d%s", &n, s);
for(int i = 0; i < n; ++i)
{
int id = s[i] - 'a';
for(int j = 0; j < 26; ++j)
{
appearance2x2[id][j] = digitsAC(appearance2x2[id][j]);
appearance2x2[j][id] = digitsBD(appearance2x2[j][id]);
}
}
//counting the results
for(int i = 0; i < 26; ++i)
{
for(int j = 0; j < 26; ++j)
{
if(i == j)continue;
if(appearance2x2[i][j] >= 4)count += ((appearance2x2[i][j] - 2) / 2);
}
}
printf("%d", count);
return 0;
}
The time complexity is O(26N), which is equal to linear.
The code can be further accelerated by making bitwise mask operations, but I left the functions simple for clearness.
Haven't tested it a lot, please tell me if you find any bugs in it!
edit: There exists problem when handling continuous appearing letters like aabbaabb
Here is an O(n) solution (counting the number of characters in the allowed character set as constant).
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
/* As used in this program, "substring" means a string that can be formed by
characters from another string. The resulting characters are not
necessarily consecutive in the original string. For example, "ab" is a
substring of "xaxxxxbxx".
This program requires the lowercase letters to have consecutive codes, as
in ASCII.
*/
#define Max 2000 // Maximum string length supported.
typedef short T1; // A type that can hold Max.
typedef int T2; // A type that can hold Max**2.
typedef long T3; // A type that can hold Max**3.
typedef long long T4; // A type that can hold Max**4.
#define PRIT4 "lld" // A conversion specification that will print a T4.
#define L ('z'-'a'+1) // Number of characters in the set allowed.
/* A Positions structure records all positions of a character in the string.
N is the number of appearances, and Position[i] is the position (index into
the string) of the i-th appearance, in ascending order.
*/
typedef struct { T1 N, Position[Max]; } Positions;
/* Return the number of substrings "aaaa" that can be formed from "a"
characters in the positions indicated by A.
*/
static T4 Count1(const Positions *A)
{
T4 N = A->N;
return N * (N-1) * (N-2) * (N-3) / (4*3*2*1);
}
/* Return the number of substrings "abab" that can be formed from "a"
characters in the positions indicated by A and "b" characters in the
positions indicated by B. A and B must be different.
*/
static T4 Count2(const Positions *A, const Positions *B)
{
// Exit early for trivial cases.
if (A->N < 2 || B->N < 2)
return 0;
/* Sum[i] will record the number of "ab" substrings that can be formed
with a "b" at the position in B->Position[b] or earlier.
*/
T2 Sum[Max];
T3 RunningSum = 0;
/* Iterate b through the indices of B->Position. While doing this, a is
synchronized to index to a corresponding place in A->Position.
*/
for (T1 a = 0, b = 0; b < B->N; ++b)
{
/* Advance a to index into A->Position where where A->Position[i]
first exceeds B->Position[b], or to the end if there is no such
spot.
*/
while (a < A->N && A->Position[a] < B->Position[b])
++a;
/* The number of substrings "ab" that can be formed using the "b" at
position B->Position[b] is a, the number of "a" preceding it.
Adding this to RunningSum produces the number of substrings "ab"
that can be formed using this "b" or an earlier one.
*/
RunningSum += a;
// Record that.
Sum[b] = RunningSum;
}
RunningSum = 0;
/* Iterate a through the indices of A->Position. While doing this, b is
synchronized to index to a corresponding place in B->Position.
*/
for (T1 a = 0, b = 0; a < A->N; ++a)
{
/* Advance b to index into B->Position where where B->Position[i]
first exceeds A->Position[a], or to the end if there is no such
spot.
*/
while (b < B->N && B->Position[b] < A->Position[a])
++b;
/* The number of substrings "abab" that can be formed using the "a"
at A->Position[a] as the second "a" in the substring is the number
of "ab" substrings that can be formed with a "b" before the this
"a" multiplied by the number of "b" after this "a".
That number of "ab" substrings is in Sum[b-1], if 0 < b. If b is
zero, there are no "b" before this "a", so the number is zero.
The number of "b" after this "a" is B->N - b.
*/
if (0 < b) RunningSum += (T3) Sum[b-1] * (B->N - b);
}
return RunningSum;
}
int main(void)
{
// Get the string length.
size_t length;
if (1 != scanf("%zu", &length))
{
fprintf(stderr, "Error, expected length in standard input.\n");
exit(EXIT_FAILURE);
}
// Skip blanks.
int c;
do
c = getchar();
while (c != EOF && isspace(c));
ungetc(c, stdin);
/* Create an array of Positions, one element for each character in the
allowed set.
*/
Positions P[L] = {{0}};
for (size_t i = 0; i < length; ++i)
{
c = getchar();
if (!islower(c))
{
fprintf(stderr,
"Error, malformed input, expected only lowercase letters in the string.\n");
exit(EXIT_FAILURE);
}
c -= 'a';
P[c].Position[P[c].N++] = i;
}
/* Count the specified substrings. i and j are iterated through the
indices of the allowed characters. For each pair different i and j, we
count the number of specified substrings that can be performed using
the character of index i as "a" and the character of index j as "b" as
described in Count2. For each pair where i and j are identical, we
count the number of specified substrings that can be formed using the
character of index i alone.
*/
T4 Sum = 0;
for (size_t i = 0; i < L; ++i)
for (size_t j = 0; j < L; ++j)
Sum += i == j
? Count1(&P[i])
: Count2(&P[i], &P[j]);
printf("%" PRIT4 "\n", Sum);
}
In the worst-case scenario, the whole string contains the same character, and in this case every indexes such that 1 <= a < b < c < d <= N will satisfy s[a] == s[c] && s[b] == s[d], hence the counter would add up to n*(n-1)*(n-2)*(n-3) / 4!, which is O(n^4). In other words, assuming the counting process is one-by-one (using counter++), there is no way to make the worst-case time complexity better than O(n^4).
Having that said, this algorithm can be improved. One possible and very important improvement, is that if s[a] != s[c], there is no point in continuing to check all possible indexes b and d. user3777427 went in this direction, and it can be further improved like this:
for(int a = 0; a < n-3; a++)
{
for(int c = a + 2; c < n-1; c++)
{
if(s[a] == s[c])
{
for(int b = a + 1; b < c; b++)
{
for(int d = c + 1; d < n; d++)
{
if(s[b] == s[d])
{
count++;
}
}
}
}
}
}
Edit:
After some more thought, I have found a way to reduce to worst-cast time complexity to O(n^3), by using a Histogram.
First, we go over the char array once and fill up the Histogram, such that index 'a' in the Histogram will contain the number of occurences of 'a', index 'b' in the Histogram will contain the number of occurences of 'b', etc.
Then, we use the Histogram to eliminate the need for the most inner loop (the d loop), like this:
int histogram1[256] = {0};
for (int i = 0; i < n; ++i)
{
++histogram1[(int) s[i]];
}
int histogram2[256];
for(int a = 0; a < n-3; a++)
{
--histogram1[(int) s[a]];
for (int i = 'a'; i <= 'z'; ++i)
{
histogram2[i] = histogram1[i];
}
--histogram2[(int) s[a+1]];
for (int c = a + 2; c < n-1; c++)
{
--histogram2[(int) s[c]];
for (int b = a + 1; b < c; b++)
{
if (s[a] == s[c])
{
count += histogram2[(int) s[b]];
}
}
}
}
Problem
It is perhaps useful for thinking about the problem to recognize that it is an exercise in counting overlapping intervals. For example, if we view each pair of the same characters in the input as marking the endpoints of a half-open interval, then the question is asking to count the number of pairs of intervals that overlap without one being a subset of the other.
Algorithm
One way to approach the problem would begin by identifying and recording all the intervals. It is straightforward to do this in a way that allows the intervals to be grouped by left endpoint and ordered by right endpoint within each group -- this falls out easily from a naive scan of the input with a two-level loop nest.
Such an organization of the intervals is convenient both for reducing the search space for overlaps and for more efficiently counting them. In particular, one can approach the counting like this:
For each interval I, consider the interval groups for left endpoints strictly between the endpoints of I.
Within each of the groups considered, perform a binary search for an interval having right endpoint one greater than the right endpoint of I, or the position where such an interval would occur.
All members of that group from that point to the end satisfy the overlap criterion, so add that number to the total count.
Complexity Analysis
The sorted interval list and group sizes / boundaries can be created at O(n2) cost via a two-level loop nest. There may be as many as n * (n - 1) intervals altogether, occurring when all input characters are the same, so the list requires O(n2) storage.
The intervals are grouped into exactly n - 1 groups, some of which may be empty. For each interval (O(n2)), we consider up to n - 2 of those, and perform a binary search (O(log n)) on each one. This yields O(n3 log n) overall operations.
That's an algorithmic improvement over the O(n4) cost of your original algorithm, though it remains to be seen whether the improved asymptotic complexity manifests improved performance for the specific problem sizes being tested.

Finding substring, but not for all inputs?

I wrote a code to find the index of the largest substring in a larger string.
A substring is found when there is an equal amount of a's and b's.
For example, giving 12 and bbbbabaababb should give 2 9, since the first appearing substring starts at index 0 and ends at index 9. 3 10 is also an answer, but since this is not the first appearing substring, this will not be the answer.
The code I made is:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
void substr(char str[], int n) {
int sum = 0;
int max = -1, start;
for (int i = 0; i < n; i++) {
if (str[i]=='a') {
str[i] = 0;
} else if(str[i]=='b') {
str[i] = 1;
}
}
// starting point i
for (int i = 0; i < n - 1; i++) {
sum = (str[i] == 0) ? -1 : 1;
// all subarrays from i
for (int j = i + 1; j < n; j++) {
(str[j] == 0) ? (sum += -1) : (sum += 1);
// sum == 0
if (sum == 0 && max < j - i + 1 && n%2==0) {
max = j - i + 1;
start = i-1;
} else if (sum == 0 && max < j - i + 1 && n%2!=0) {
max = j - i + 1;
start = i;
}
}
}
// no subarray
if (max == -1) {
printf("No such subarray\n");
} else {
printf("%d %d\n", start, (start + max - 1));
}
}
/* driver code */
int main(int argc, char* v[]) {
int n; // stores the length of the input
int i = 0; // used as counter
scanf("%d", &n);
n += 1; // deals with the /0 at the end of a str
char str[n]; // stores the total
/* adding new numbers */
while(i < n) {
char new;
scanf("%c", &new);
str[i] = new;
++i;
}
substr(str, n);
return 0;
}
It works for a lot of values, but not for the second example (given below). It should output 2 9 but gives 3 10. This is a valid substring, but not the first one...
Example inputs and outputs should be:
Input Input Input
5 12 5
baababb bbbbabaababb bbbbb
Output Output Output
0 5 2 9 No such subarray
You have several problems, many of them to do with arrays sizes and indices.
When you read in the array, you want n characters. You then increase n in oder to accomodate the null terminator. It is a good idea to null-terminate the string, but the '\0' at the end is really not part of the string data. Instead, adjust the array size when you create the array and place the null terminator explicitly:
char str[n + 1];
// scan n characters
str[n] = '\0';
In C (and other languages), ranges are defined by an inclusive lower bound, but by an exclusive upper bound: [lo, hi). The upper bound hi is not part of the range and there are hi - lo elements in the range. (Arrays with n elements are a special case, where the valid range is [0, n).) You should embrace rather than fight this convention. If your output should be different, amend the output, not the representation in your program.
(And notw how your first example, where you are supposed to have a string of five characters actually reads and considers the b in the 6th position. That's a clear error.)
The position of the maximum valid substring does not depend on whether the overall string length is odd or even!
The first pass, where you convert all "a"s and "b"s to 0's and 1's is unnecessary and it destroys the original string. That's not a big problem here, but keep that in mind.
The actual problem is how you try to find the substrings. Your idea to add 1 for an "a" and subtract one for a "b" is good, but you don't keep your sums correctly. For each possible starting point i, you scan the rest of the string and look for a zero sum. That will only work, if you reset the sum to zero for each i.
void substr(char str[], int n)
{
int max = 0;
int start = -1;
for (int i = 0; i + max < n; i++) {
int sum = 0;
for (int j = i; j < n; j++) {
sum += (str[j] == 'a') ? -1 : 1;
if (sum == 0 && max < j - i) {
max = j - i;
start = i;
}
}
}
if (max == 0) {
printf("No such subarray\n");
} else {
printf("%d %d\n", start, start + max);
}
}
Why initialize max = 0 instead of -1? Because you add +1/−1 as first thing, your check can never find a substring of max == 0, but there's a possibility of optimization: If you have already found a long substring, there's no need to look at the "tail" of your string: The loop condition i + max < n will cut the search short.
(There's another reason: Usually, sizes and indices are represented by unsigned types, e.g. size_t. If you use 0 as initial value, your code will work for unsigned types.)
The algorithm isn't the most efficient for large arrays, but it should work.

C - How to find a winner of a norts and crosses

I am currently making a tic tac toe game using C. I am making a function Check(char sign) that i can call after each player makes a move which will check for a winner. I need help as the tic tac toe game can be played on different sized boards (3 up to 12).
I made the following code which can be used to find the winner of a 3 x 3 game:
**//For Rows**
for(i=0; i<3; i++)/***sign is the player symbol e.g. 'X'OR'O'***/
if(board[i][0] == sign && board[i][0] == board[i][1] && board[i][1] == `
board[i][2]){`
printf("the winner is %c", sign );
return 1;
}
**//For Columns**
for(i=0; i<3; i++)/***sign is the player symbol e.g. 'X'OR'O'***/
if(board[0][i] == sign && board[0][i] == board[1][i] && board[1][i] ==
board[2][i]){
printf("the winner is %c", sign );
return 1;
}
**//For Diagnal 1 \**
if(board[0][0] == sign && board[0][0] == board[1][1] && board[1][1] ==
board[2][2]){
printf("the winner is %c", sign );
return 1;
}
**//For Diagnal 2 /**
if(board[0][2] == sign && board[0][2] == board[1][1] && board[1][1] ==
board[2][0]){
printf("the winner is %c", sign );
return 1;
}
However as I mentioned my game can be played on different board sizes which is chosen by user input at the beginining of the game. This size is stored in a vreriable "int Size". How would I alter the above code to work with any size board?
I know that i could create a list if statements each one containing code for a specific board size but this seems like the incorrect and least efficient way of doing so.
I hope someone can help. Any help will be greatly appreciated.
Thank you.
Note that the if statement for each of your 4 cases could be written using a loop; for example, the first one could be written as:
int win = (board[i][0] == sign); // assume a win until we find out otherwise
// loop over the pairs to compare until we find a mismatch or exhaust them all
for ( int j = 1; (win == 1) && (j < size); j++ )
if ( board[i][j-1] != board[i][j] )
win = 0;
if ( win ) {
printf("the winner is %c", board[i][0] );
return 1;
}
Change which 2 elements of board are being compared, and the initialization of win, for each of the other 3 cases.
You could have three function, one that iterates through rows, one that iterates through cols, and one that iterates through the diagonal pair.
/* If the sign player won, return 1. Else return 0. */
int rowWinnerCheck(int n, char sign, char board[n][n]){
for(int i = 0; i < n; i++){
int sameSign = 1;
for(int j = 0; j < n; j++){
char letter = board[i][j];
if(letter != sign){
sameSign = 0;
}
}
if(sameSign == 1){
return 1;
}
}
return 0;
}
colWinnerCheck would be similar to how rowWinnerCheck is implemented.
Verifying if a player won diagonally is also similar.
/* This function checks if a player won diagonally from the top-left to bottom-right*/
int diagonalWinnerCheck(int n, char sign, char board[n][n]){
for(int i = 0; i < n; i++){
int sameSign = 1;
char letter = board[i][i];
if(letter != sign){
return 0;
}
}
return 1;
}
Solution without loops
Here is a possible solution without loops, which I believe scales better for a larger n.
I'll leave the C out for you to complete, and I'll just explain the concept for determining the winner on an n x n board. The code you gave uses loops to compare the contents on the board. This solution doesn't depend on loops as it makes use of a mathematical representation to determine the winner.
Board Representation
We first need to have a representation for the board using a 2D array of integer of size n x n. We will also make everything zero in the board from the start.
int board[n][n];
memset(board, 0, sizeof(board[0][0]) * n * n);
We define the letters that are used, X and O, and assign them to be the constants, 1 and -1 respectively. Note: 1, -1 were choose arbitrarily, but for the solution to work, they need to be some integer k, -k.
We define four more items for the solution. Two integer arrays, one to represent the rows and one for the cols, and two integers for the two diagonals.
int rows[n] = {0};
int cols[n] = {0};
int leftDiag = 0;
int rightDiag = 0;
How it Works
Let's assume a new game was started on a 3x3 board. I play X, and you play O. I make my move on location (2,0). The program first checks the board location at (2,0) and sees if it's zero. If it is, set it to be X, else raise an error since that spot has been played already. The program also adds X to rows[2] and cols[0].
You play O at (1,1). First check if board[1][1] has been already played, if not, set it to be O. Add O to rows[1] and cols[1]. Here is a special case. When you player played O on the diagonal i.e. for a move (a,b) a == b or a == (n-b-1), add O to the appropriate diagonal variable. Since (1,1) belongs to both diagonal, add O to leftDiag and rightDiag.
We alternate turns until one of these conditions are met:
A location i in rows such that rows[i] == n * X. This means X
won by filling the ith row. A location j in rows such that
rows[j] == n * O. This means O won
A location i in cols such that cols[i] == n * X. This means X
won by filling the ith col. A location j in cols such that
cols[j] == n * O. This means O won
leftDiag == n * X or rightDiag == n * X, X won. leftDiag == n * O or rightDiag == n * O, O won.
n*n moves have been made, but the previous conditions were
never met. This mean the game ended in a tie.
Notice that at the end, we don't need to iterate through rows, cols, or diagonals to verify if someone won. If a row, col, or diagonal, sums up throughout the game to be n * Character, then that Character player won. This reduces the complexity of verifying a winner from O(n^2) to O(1). The trade off is just O(n) more space.

How can I reduce the runtime?

Here is a link to the problem I'm trying to solve: http://acm.timus.ru/problem.aspx?space=1&num=1086
Here is my approach:
#include <stdio.h>
#include <math.h>
int main()
{
int n, i, m, p;
scanf("%d", &n);
for(i = 0; i < n; i++)
{
scanf("%d", &m);
p = find_prime(m);
printf("%d\n", p);
}
return 0;
}
int find_prime(int a)
{
int i, p = 1, t, prime[15000], j;
prime[0] = 2;
for(i = 0; i < a; )
{
if(p == 2)
{
p++;
}else
{
p = p + 1;
}
t = 0;
for(j = 0; prime[j] <= sqrt(p); j++)
{
if(p%prime[j] == 0 && p != 2)
{
t = 1;
break;
}
}
if(t != 1)
{
i++;
prime[i] = p;
}
}
return p;
}
I know the algorithm is fine and it produces the correct answer. But I always get "Time Limit Exceeded". I can't get the runtime download to 2 seconds. It's always equal to 2.031 seconds. I have tried few other approaches, for example, I iterated through all the numbers until I found the mth prime number, I tried skipping the even integers greater than 2 but I still get 2.031 seconds.
What should I do?
Your buffer for prime numbers doesn't need to be a local variable that's recalculated every time.
You can try to memoize by storing the buffer in the global scope and using a global counter to keep track of how many primes you have already calculated until now and which number was the maximum number requested.
If the next number that's requested from you is smaller than the previous maximum, you should fall back to the corresponding pre-calculated number. If the next number is larger than the previous maximum, make it the new maximum - and also try to start calculating from where you last left off.
Remove
if(p == 2)
{
p++;
}else
{
p = p + 1;
}
and replace it with
p++
as I understand it,
the problem is to find the next prime greater that the sum of all the prior input numbers.
That means there are certain expectations.
1) the sum of the prior input numbers is available in find_prime().
2) for simplification, the last found prime number is available in find_prime().
Neither of these expectations are implemented.
Then there is that 60 thousand byte array on the stack in find_prime().
Suggest moving that to a file global position and including a 'static' modifier.
move the prior sum of inputs to a file global location, so it is always available.
for overall speed,
calculate all the primes in the array as a first thing, thereby filling the array with prime values. then
1) add new input to sum,
2) index into array using sum.
3) return value found in array.

Resources