2

Just started out with tinyduino(tinycircuits) and I am trying to develop a hangman game. I have this chunk of code that randomly picks a word from my array. I've omitted some code for readability.

char* words[] = {"Fortran", "Cobol", "Python", "Java", "Javascript", "Kotlin", "Swift", "Golang", "Typescript"};
long word1;
char* generatedWord1;
int k;
int i;
int wordLength;

void setup(void) {
  randomSeed(analogRead(5));
  hangman();

void hangman(){
  word1 = random(sizeof(words)/sizeof(char*));
  k = 1;

  if (k == 1) {
    display.setCursor(10, 50);
    generatedWord1 = words[word1];

    for (i = 0; generatedWord1[i]; i++) {
      generatedWord1[i] = "_";
    }
}

When I upload this to my board, it would not display the underscores based on the randomly chosen word's length. Re-uploading it does not work and usually requires me to reboot the tinyduino after that. Hence, I concluded my code crashes it. But, when I replace the generatedWord1[i] = "_"; in the for loop with display.print("_"); the program carries on and it prints the correct amount of underscores.

However, this does not fit my purpose of replacing the letters with underscores, as display.print("_"); just prints underscores based on the random word's length instead. I need it to be in a manner where I can still compare a user input against the random word and replace the underscore if the letter input matches any letters in the word.

What and how do I modify my code to work as such?

Note: For the context of this whole sketch, I am trying to use get an input from a user through bluetooth and compare whether or not the input matches any of the letters from the random word and then replace the underscore with the input letter. Completing the word means winning the game as per hangman.

EDIT: I've added the whole sketch below for reference just in case I missed out something important for my question.

#include <SPI.h>
#include <STBLE.h>
#include <Wire.h>
#include <SPI.h>
#include <TinyScreen.h>
#include <string.h>

//Debug output adds extra flash and memory requirements!
#ifndef BLE_DEBUG
#define BLE_DEBUG true
#endif

#if defined (ARDUINO_ARCH_AVR)
#define SerialMonitorInterface Serial
#elif defined(ARDUINO_ARCH_SAMD)
#define SerialMonitorInterface SerialUSB
#endif


uint8_t ble_rx_buffer[21];
uint8_t ble_rx_buffer_len = 0;
uint8_t ble_connection_state = false;
#define PIPE_UART_OVER_BTLE_UART_TX_TX 0

TinyScreen display = TinyScreen(TinyScreenDefault);

// declarations
const char* words[] = {"Fortran", "Cobol", "Python", "Java", "Javascript", "Kotlin", "Swift", "Golang", "Typescript"};
long word1;
const char* generatedWord1;
int k;
int i;
int length;





void setup(void) {
  Serial.begin(9600);
  randomSeed(analogRead(5));
  BLEsetup();

  Wire.begin();
  display.begin();
  display.setBrightness(10);
  hangman();
}

void hangman(){
  display.clearScreen();
  display.setFont(thinPixel7_10ptFontInfo);
  display.setCursor(10,20);

  display.print("Ready to hang?");
  delay(2000);

  display.setCursor(5,20);
  display.print("generating word...");
  delay(1000);

  display.clearScreen();

  // word1 is my index, so printing out word1 will give you a random index from the array
  word1 = random(sizeof(words)/sizeof(char*));
  k = 1;

  if (k == 1) {

    display.setCursor(10, 50);
    generatedWord1 = words[word1];

    length = strlen(generatedWord1);
    char user_input[length+1] = "";

    for (i=0; i < length; i++) {
      user_input[i] = "";
      user_input[length] = 0;
    }   

    display.setCursor(30, 30);
    display.print("|");
    
    display.setCursor(30, 20);
    display.print("|");

    display.setCursor(30, 10);
    display.print("|");

    display.setCursor(30, 0);
    display.print("____");

    // remember to move this away from the if statement
    // If user guesses wrong letter (1)
    display.setCursor(50, 10);
    display.print("|");

    // If user guesses wrong letter (2)
    display.setCursor(49, 15);
    display.print("o");

    // If user guesses wrong letter (3)
    display.setCursor(50, 25);
    display.print("|");

    // If user guesses wrong letter (4)
    display.setCursor(43, 21);
    display.print("-");

    // If user guesses wrong letter (5)
    display.setCursor(54, 21);
    display.print("-");

    // IF user guesses wrong letter (6)
    display.setCursor(43, 30);
    display.print("/");

    // If user guesses wrong letter (7)
    display.setCursor(52, 30);
    display.print("\\"); 
  }

}


void displayScreen(char phone_input[21]) {

  display.clearScreen();

  int width=display.getPrintWidth(phone_input);
  display.setFont(thinPixel7_10ptFontInfo);
  display.setCursor(48-(width/2),32);
  display.fontColor(TS_8b_White,TS_8b_Black);
  display.print(phone_input);
}

void loop() {
  aci_loop();//Process any ACI commands or events from the NRF8001- main BLE handler, must run often. Keep main loop short.
  if (ble_rx_buffer_len) {//Check if data is available
    delay(10);
    displayScreen((char*)ble_rx_buffer);
    ble_rx_buffer_len = 0;//clear afer reading
  }

}



1 Answer 1

4

Funny, I just wrote an answer about the problem, that you have here, about 45min ago. And the problem is, that you are trying to modify a string literal. That is undefined behavior in C++.

When you declare your words array with

char* words[] = {"Fortran", "Cobol", "Python", "Java", "Javascript", "Kotlin", "Swift", "Golang", "Typescript"};

you are actually defining an array of pointers. The compiler will place these strings in the RAM and place their memory addresses in the array. Then you are declaring another char pointer and set it to one of the words:

char* generatedWord1;
generatedWord1 = words[word1];

So now generatedWord1 will also point to one of the string literals. And then you are trying to write to this string literal:

generatedWord1[i] = "_";

But - as already mentioned - string literals are not meant to be changed. Trying so is undefined behavior and yes, it can lead crashes.

What to do now? You can define an array of char (not char pointers) as a buffer and fill it with the number of underscores that equals the length of the string literal. When defining the array you can choose the length of the string literal (plus 1 to accommodate the null character) as size, though - depending on your program flow, that you didn't show - it might be better to just declare a buffer with a fixed size bigger than the longest of your words.

You can get the length of the string literal with the strlen() function:

int length = strlen(generatedWord1);

Defining the char buffer:

char user_input[length+1] = "";

Filling it with underscores

for(int i=0;i<length;i++){
    user_input[i] = '_';
}
user_input[length] = 0; // terminating null character

I still can't get the desired output I would like. Also it keeps throwing me an error of invalid conversion from 'const char*' to 'char'.

The following part is wrong:

for (i=0; i < length; i++) {
    user_input[i] = "";
    user_input[length] = 0;
}   

First: You misunderstood what is in the loop and what not. I changed my snipped above to use curly braces. Terminating the string with the null character can be done outside of the loop.

Second: You wanted to fill the buffer with underscores. If you look at my code snippet I assigned an underscore here.

The warning that you are getting from the compiler also lies in this line. You have used double quotes. Those mark a string literal. But here we are assigning a single character, so you need to use single quotes, which mark single characters.

The message invalid conversion from const char* to char means, that you used a string literal (which is a pointer to a constant character) and assigned it to a single character variable (one element of the char array).

7
  • Hi @chrisl, after trying out the code you've provided, I still can't get the desired output I would like. Also it keeps throwing me an error of invalid conversion from 'const char*' to 'char'. Nov 11, 2022 at 12:52
  • You can add your new code to the end of your question. Then I will have a look at it.
    – chrisl
    Nov 11, 2022 at 13:11
  • 2
    @IsaacAgatep: 1. chrisl's code works for me. If it doesn't work for you, show us how you integrated it into your program. 2. The “invalid conversion” message is a warning, not an error. You can fix it by using the type const char * (meaning: “pointer to immutable characters”) for all pointers meant to point to string literals. In this program, both words and generatedWord1 need the const qualifier. Nov 11, 2022 at 13:13
  • Hello @Edgar Bonet, I've tried both of your suggestions but to no avail. I've added my whole sketch at the bottom of the question just in case I missed out anything important that might be causing my mistakes. Nov 11, 2022 at 13:22
  • @IsaacAgatep I have added a paragraph to my answer to address this.
    – chrisl
    Nov 11, 2022 at 13:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.