Chris Grooms
Chris Grooms

Reputation: 51

Weird non-ASCII Characters in Console Output

The following is one example of several that seemingly randomly the other night started resulting in weird ascii characters being display in console. For example the player.level displays a smiley face ascii character. Some displays simply several blank spaces, others display single weird ascii characters.

Image example:

The code worked fine and I was having some compiler issues that I fixed. Around the same time I changed a lot of "int" data types to uint8_t etc. Once I fixed the compiler issues I started seeing this new issue.

Language: C++(11) (CodeBlocks IDE)

//randstats.cpp

#include "encounter.h"
#include "player.h"
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <iostream>
#include "createchar.h"
#include "randstats.h"

boost::random::mt19937 gen;

player_vars randStats()
{
    /*
        Randomize new characters stats if the player chooses to do so rather than manually assign them.
        Also going to use this to test against random encounters for balance purposes.
    */

    player.str = 8;
    player.dex = 8;
    player.con = 8;
    player.intel = 8;
    player.wis = 8;
    player.cha = 8;
    uint8_t remaining_points = 25; // starting pts to hand out freely
    uint8_t total_possible = player.str + player.dex + player.con + player.intel + player.wis + player.cha + remaining_points; // max points to assign

    /*
        Basically the whole purpose of this is to pick random numbers and spread them out among random stats.
        The stats will not be higher than total_possible + remaining points (assigned above).
    */

    while (remaining_points > 0)
    {

        boost::random::uniform_int_distribution<> dist13(1, 3);
        uint8_t random_number = dist13(gen);

        uint8_t x = 0;
        // Get current or total points currently assigned
        uint8_t totalpts = player.str + player.dex + player.con + player.intel + player.wis + player.cha;

        //if we're in range and won't go over
        if ((totalpts < total_possible) && ((random_number + totalpts) < total_possible))
        {
            x = random_number;
            // remaining_points - x;
        }
        //if we're going to go over, only assign the number of points from random that will stop us at 73 points
        else if ((random_number + totalpts) > total_possible)
        {
            // Since we're going over the max total points, how many do we assign to a stat?
            uint8_t y = totalpts - total_possible;
            x = abs(y);
            remaining_points = 0; //force this because we exceeded
        }

        //random numbering choosing which stat the points go to
        boost::random::uniform_int_distribution<> dist16(1, 6);

        switch(dist16(gen))
        {
        case 1 :
            player.str += x;
            break;
        case 2 :
            player.dex += x;
            break;
        case 3 :
            player.con += x;
            break;
        case 4 :
            player.intel += x;
            break;
        case 5 :
            player.wis += x;
            break;
        case 6 :
            player.cha += x;
            break;
        default :
            break;
        }

    }

    //print the new results
    std::cout << "\nNew Character Stats: \n";
    std::cout << "Strength: [" << player.str << "]" << std::endl;
    std::cout << "Dexterity: [" << player.dex << "]" << std::endl;
    std::cout << "Constitution: [" << player.con << "]" << std::endl;
    std::cout << "Intelligence: [" << player.intel << "]" << std::endl;
    std::cout << "Wisdom: [" << player.wis << "]" << std::endl;
    std::cout << "Charisma: [" << player.cha << "]" << std::endl;
    std::cout << "Total Points Assigned: " << player.str + player.dex + player.con + player.intel + player.wis + player.cha << std::endl;

    std::cout << "Accept these results? [y/n]: ";
    char selection;
    std::cin >> selection;

    switch(selection)
    {
    case 'Y' :
    case 'y' :
        createPlayer();
        break;
    case 'N' :
    case 'n' :
        randStats();
        break;
    default:
        std::cout << "\nInvalid response. Keeping results.\n";
        createPlayer();
    }

    return player;
}//close function


   //player.h
   #ifndef PLAYER_H_INCLUDED
   #define PLAYER_H_INCLUDED

   #include <string>
   #include <cstdint>

   struct player_vars {
    std::string alias;
    uint8_t current_level;
    bool is_alive; // alive or dead
    uint8_t str;
    uint8_t dex;
    uint8_t con;
    uint8_t intel;
    uint8_t wis;
    uint8_t cha;
    uint32_t gold;
    std::string profileName; // may use
    int16_t hitpoints; // current HP dependent on class, level, feats, con, etc.
    uint8_t size_id; // character size
    uint8_t class_id; // check comments
    uint8_t base_attack_bonus; // dependent on player's class
    uint8_t weapon_id; // check comments

    uint8_t getHitPoints();

    }; extern player_vars player;

Upvotes: 1

Views: 2191

Answers (1)

Jonathon Reinhart
Jonathon Reinhart

Reputation: 137537

uint8_t is usually typedef'd to unsigned char. This works great in C.

C++'s stream operators are seeing your unsigned char values and trying to print them as characters, instead of converting the integer to a string. So what you're seeing is whatever the particular terminal/console decides to display for that character code.

Save yourself the trouble, and just stick with int. We're only talking 4 bytes instead of 1. And not having to worry about the awfully tiny limit of 255.

If you really want to store your data as uint_8, you'll want to cast to int before streaming it to cout:

std::cout << "Strength: [" << (int)player.str << "]" << std::endl;

Upvotes: 2

Related Questions