'printing 2 characters and keeping them through dynamically allocated 2d array

Hello I am trying to print something like this with 2d array. Note that when user enters the same number, character should be printed above existing char.

EXPECTED RESULTS:

Input 1: 3  //user1 inputs 3

****
****
**x*

Input 2: 1 //user2 inputs 1

****
****
y*x*

Input 3: 1 //user1 inputs 1

****
x***
y*x*

current results:

enter first: 3
3***
***
**x
enter second: 1
1******
******
xx****
enter first: 2
2*********
*********
***xxx***

But keeping printed values on its previous places. The problem is that they don't get printed in right order. And also it seems that I haven't done the best job with 2d array which is dynamically allocated. Here is something what I've tried:

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

int num(int term)
{
  int number1;
  int number2;
  if(term==1)
  {
    scanf("%d", &number1);
    return number1;
  }
  if (term==2)
  {
     scanf("%d", &number2);
    return number2;
  }
  return 0;
}
void function(int a, int b, int result[], int size)
{

  int i = 0;
  int j = 0;
  int desired_num = 0;
  int count = 0;
  int *arr[a];

  for (i = 0; i < a; i++)
    arr[i] = (int *)malloc(a * sizeof(int));

  for (i = 0; i < a; i++)
    for (j = 0; j < b; j++)
      arr[i][j] = ++count;

  for (i = 0; i < a; i++)
  {
    for (j = 0; j < b; j++)
    {
      for (int counter = 0; counter < size; counter++)
      {
        if (arr[i][j] == arr[a - 1][result[counter] - 1])
        {
          arr[i][j] = desired_num;
        }

        if (arr[i][j] == desired_num)
        {
          printf("%s", "x");
        }
        else
        {
          printf("*");
        }
      }
    }
    printf("\n");
  }
}
int main()
{
  int counter = 1;
  int i = 0;
  int given_number;
  int array[20];
  for (;;)
  {
    if (counter % 2 != 0)
    {
      printf("enter first: ");
      given_number = num(1);
      printf("%d", given_number);
    }
    else
    {
      printf("enter second: ");
      given_number = num(2);
      printf("%d", given_number);
    }
    array[i] = given_number;
    function(3, 3, array, counter);
    counter++;
  }

  return 0;
}


Solution 1:[1]

array[i] = given_number;

i is never changed from the value of 0. You are only ever overwriting the first element of array each iteration. The other 19 elements remain in an indeterminate state.

counter and array are passed to function, as size and result respectively:

This means as size is incremented, it is used as a bounds for accessing elements of result; elements that contain indeterminate values.

for (int counter = 0; counter < size; counter++)
{
    if (arr[i][j] == arr[a - 1][result[counter] - 1])

This will surely lead to Undefined Behaviour as those indeterminate values are used to index arr, effectively accessing random memory offsets. This fact alone makes it hard to reason about the output you are seeing, as really anything is valid.


While perfectly valid, the variable-length array of dynamic allocations is a somewhat perplexing choice, especially considering you fail to free the memory allocated by malloc when you are done with it.

int *arr[a];

for (i = 0; i < a; i++)
    arr[i] = (int *)malloc(a * sizeof(int));

int arr[a][b]; would work, given a and b are not stack-crushingly large values (or non-positive). You are, or would be, bounded by the size of array in main anyway.


The triply nested loop is confused at best. There is only logic for printing the x and * characters, so you obviously will never see a y.

For each element of arr, you iterate through each element of result. If the current element of arr equals the value of the column selected by the current value of result ([result[counter] - 1]) in the last row (arr[a - 1]) you print x, otherwise *.

Again UB from utilizing indeterminate values of result, but you can see you are printing a * b * size characters, plus newlines, each iteration.

This is severely flawed.


Some other things of note:

The two branches of the if .. else statement in the num function do the exact same thing, just with different identifiers.

The two branches of the if .. else statement in main are identical, other than the first printf in each, and the integer value passed to num, which have the same effect.

This means the only thing that needs to branch is the printf argument.

A generic function for getting an integer would work fine

int get_num(void)
{
    int n;

    if (1 != scanf("%d", &n)) {
        fprintf(stderr, "Could not read input.\n");
        exit(EXIT_FAILURE);
    }

    return n;
}

for use inside main

if (counter % 2 == 0)
    printf("enter first: ");
else
    printf("enter second: ");

given_number = get_num();

A small issue: printf("%d", given_number); is muddling the output slightly.


There is no reason to repeatedly generate the array. Initialize an array in main to serve as the state of the program. Over time, fill it with the users' selections, and simply print the array each iteration.

Make sure to always check the return value of scanf is the expected number of conversions, and ensure the integers provided by the users will not access invalid memory.

Here is a cursory example.

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

#define EMPTY '*'
#define PLAYER_ONE 'X'
#define PLAYER_TWO 'O'

int get_num(void)
{
    int n;

    if (1 != scanf("%d", &n)) {
        fprintf(stderr, "Could not read input.\n");
        exit(EXIT_FAILURE);
    }

    return n;
}

int main(void)
{
    const size_t rows = 6;
    const size_t cols = 7;

    char board[rows][cols + 1];

    memset(board, EMPTY, sizeof board);

    /* our rows are now strings */
    for (size_t i = 0; i < rows; i++) {
        board[i][cols] = '\0';
        puts(board[i]);
    }

    unsigned char turn = 1;

    while (1) {
        printf("Player %s, Enter column #(1-%zu): ",
                turn & 1 ? "One" : "Two", rows);

        int input = get_num();

        if (1 > input || input > cols) {
            printf("Invalid column [%d]. Try again...\n", input);
            continue;
        }

        size_t sel = input - 1;

        if (board[0][sel] != EMPTY) {
            printf("Column [%d] is full! Try again...\n", input);
            continue;
        }

        size_t n = rows;

        while (n--) {
            if (board[n][sel] == EMPTY) {
                board[n][sel] = turn & 1 ? PLAYER_ONE : PLAYER_TWO;
                break;
            }
        }

        for (size_t i = 0; i < rows; i++)
            puts(board[i]);

        turn ^= 1
    }
}

Solution 2:[2]

Convert the original list to a list of strings where the original strings are split.

data = ['12,34,212,90,1,8','901,2,1,8,44,1,1','77,32,11,230,894','78,23,45,89,12,20']
t = []
{t.extend(e.split(',')) for e in data}
print(t)

The resulting list would look like this:

['12', '34', '212', '90', '1', '8', '901', '2', '1', '8', '44', '1', '1', '77', '32', '11', '230', '894', '78', '23', '45', '89', '12', '20']

Then you simply use the count() method of the list. For instance:

t.count('1')

Will give you:

4

Solution 3:[3]

First, convert the list of string into a list containing just the values.

res = []
for i in data:
    res += i.split(',')

Then we can use python's Counter to count each value:

from collections import Counter

res = []
data = ['12,34,212,90,1,8','901,2,1,8,44,1,1','77,32,11,230,894','78,23,45,89,12,20']

for i in data:
    res += i.split(',')

res = Counter(res)
for key in res:
    print(f"{key} exist {res[key]} times")

Try it online!


Output:

12 exist 2 times
34 exist 1 times
212 exist 1 times
90 exist 1 times
1 exist 4 times
8 exist 2 times
901 exist 1 times
2 exist 1 times
44 exist 1 times
77 exist 1 times
32 exist 1 times
11 exist 1 times
230 exist 1 times
894 exist 1 times
78 exist 1 times
23 exist 1 times
45 exist 1 times
89 exist 1 times
20 exist 1 times

Solution 4:[4]

using Counter and join

# your code goes here
data = ['12,34,212,90,1,8','901,2,1,8,44,1,1','77,32,11,230,894','78,23,45,89,12,20']
from collections import Counter
result = Counter(map(int, ','.join(data).split(',')))
print(result)

# output

# Counter({1: 4, 12: 2, 8: 2, 34: 1, 212: 1, 90: 1, 901: 1, 2: 1, 44: 1, 77: 1, 32: 1, 11: 1, 230: 1, 894: 1, 78: 1, 23: 1, 45: 1, 89: 1, 20: 1})

Solution 5:[5]

Let's break down in two steps.

1. Retrieve the numbers

You have a list of strings and each string has numbers separated by commas that need to be parsed.

>>> numbers = [int(x) for s in data for x in s.split(',')]
>>> numbers
[12, 34, 212, 90, 1, 8, 901, 2, 1, 8, 44, 1, 1, 77, 32, 11, 230, 894, 78, 23, 45, 89, 12, 20]

tip: the correct order of the for statements when flatting in a comprehension is the same as if you would iterate using two nested fors.

2. Count!

>>> from collections import Counter
>>> Counter(numbers)
Counter({1: 4, 12: 2, 8: 2, 34: 1, 212: 1, 90: 1, 901: 1, 2: 1, 44: 1, 77: 1, 32: 1, 11: 1, 230: 1, 894: 1, 78: 1, 23: 1, 45: 1, 89: 1, 20: 1})

Solution 6:[6]

d = dict()
for i in data:
    temp = i.split(",")
    for num in temp:
        n = int(num)
        if n not in d: 
            d[n] = 1
        else: 
            d[n] += 1

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Oka
Solution 2
Solution 3 0stone0
Solution 4 sahasrara62
Solution 5 Helio Meira Lins
Solution 6