Home > Back-end >  Finding a way to pass inputs through a file in C and store output in a file too
Finding a way to pass inputs through a file in C and store output in a file too

Time:09-17

The code works like this, you type in a combination and another combination which is your desired combination, for example:

[1

The first two lines are the combination, the next two are the desired combination, the fifth line is the minimum number of moves required, then the program displays the minimum number of moves required to get the combination, but now I'm required to make the program work through the terminal such that a file is passed in as input.txt then the result will be saved as output.txt, and can be run from terminal as app.exe input.txt output.txt, the code works fine right now but finding a way to read inputs from a file is problematic from

#include <iostream>
#include <fstream>
#include <queue>
#include <map>
#include <algorithm>

using namespace std;

struct State {
    char a[2][4];

    static State read() {
        State s;
        for (int i = 0; i < 2;   i) {
            for (int j = 0; j < 4;   j) {
                cin >> s.a[i][j];
            }
        }
        return s;
    }

    State shift(int di, int dj) const {
        State next = *this;

        for (int i = 0; i < 2;   i) {
            for (int j = 0; j < 4;   j) {
                if (next.a[i][j] == '#') {
                    int ni = di   i;
                    int nj = dj   j;

                    if (ni >= 0 && ni < 2 && nj >= 0 && nj < 4) {
                        swap(next.a[i][j], next.a[ni][nj]);
                    }
                    return next;
                }
            }
        }
    }
};

int compare(const State& left, const State& rigth) {
    for (int i = 0; i < 2;   i) {
        for (int j = 0; j < 4;   j) {
            if (left.a[i][j] != rigth.a[i][j]) {
                return left.a[i][j] - rigth.a[i][j];
            }
        }
    }
    return 0;
}

bool operator < (const State& left, const State& rigth) {
    return compare(left, rigth) < 0;
}
bool operator == (const State& left, const State& rigth) {
    return compare(left, rigth) == 0;
}

int main(int argc, char** argv)
{
    if (argc < 3) {
        perror("Not enough arguments");
        return 1;
    }

    string filename("input.txt");
    vector<char> bytes;

    FILE* input_file = fopen(filename.c_str(), "r");
    if (input_file == nullptr) {
        return EXIT_FAILURE;
    }

    unsigned char character = 0;
    while (!feof(input_file)) {
        character = getc(input_file);
        cout << character << "-";
    }


    State start = State::read();
    State finish = State::read();

    map<State, int> len;
    queue<State> q;
    len[start] = 0;
    q.push(start);

    while (!q.empty()) {
        State cur = q.front();
        q.pop();

        if (cur == finish) {
            cout << len[cur];
            return 0;
        }

        for (int i = -1; i < 2;   i) {
            for (int j = -1; j < 2;   j) {
                if (i * i   j * j == 1) {
                    State next = cur.shift(i, j);
                    if (len.count(next) == 0) {
                        len[next] = len[cur]   1;
                        q.push(next);
                    }
                }
            }
        }
    }
    cout << -1;
}

CodePudding user response:

You need to read about and understand the C iostream facilities. Please study them.

There you can read about the extraction operator >> and the insertion operator <<.

And what you need to do, is to overwrite the extraction operator for your class and the use this instead of your "read" function.

Because the extraction operator works on all istreams and childs, you can basically read from everything. Please see this picture:

enter image description here

Taken from here. There you can see that there is basically no difference between an istream like std::cin or a file stream or even a string stream.

So, if you have overwritten the extraction operator for your class State, then you can write

State state;
std::cin >> state;

//or, if you have an std::ifstream ifs
ifs >> state;

and so on. You will understand that pattern. You can see an implementation example in the code example below.

I had a hard time to understand your description of the orignal problem. It was desribed rather cryptic. Basically you have a game board with symbols on it and one field is free. Then you can shift one symbol to this free field. This means swap the symbol with the empty field. The goal is, to reach a given target pattern with many steps of shifting/swapping.

Additionally you made some strange or complicated implementations in your code.

And, there was no line of comment and also the variable names are not that "readable".

Because of all of the above, I created a new implementation based on your algorithm, with some minor improvements.

Please see one possible improved solution below:

#include <iostream>
#include <fstream>
#include <array>
#include <algorithm>
#include <map>
#include <queue>
#include <utility>

// -----------------------------------
// Some basic definitions
// Dimensions of the game board
constexpr size_t NumberOfRows {2u};
constexpr size_t NumberOfColumns {4u};
// Character to denote an empty field
constexpr char EmptyField {'#'};

// -----------------------------------------------------------------
// Directions will be given as offsets in row and column coordinates
using Direction = std::pair<const int, const int>;
// directions for                           up,    down  left   right
constexpr std::array<Direction, 4> Moves{{ {-1,0},{1,0},{0,-1},{0,1} }};

// ----------------------------------------------------------
// Safeguarding minimum requirements for gameBoard dimensions
static_assert ((NumberOfRows>1)&&(NumberOfColumns>1),"Error. Wrong game board dimensions\n");
// Borders of the gameBoard
constexpr int BorderLeft {0};
constexpr int BorderUp {0};
constexpr int BorderRight{NumberOfColumns - 1};
constexpr int BorderBottom{NumberOfRows - 1 };

// Make reading/writing simpler by using an alias
using GameBoard = std::array<std::array<char, NumberOfColumns>, NumberOfRows>;
    

    
// ---------------------------------------  
// A game board at a certain point in time
class GameState {
    // The game board, stored as a 2d array
    GameBoard gameBoard{};
public:

    // Create a new GameState with the EmptyField shifted to a new position 
    GameState swapPosition(const Direction& direction);
    
    // Extractor operator. Read a gameBoard from a stream
    friend std::istream& operator >> (std::istream& is, GameState& gs) {
        // Read character for all rows and columns
        for (auto& row : gs.gameBoard) for (char& c : row) is >> c;
        return is;
    }
    
    // Overwrite less than operator for std::map
    bool operator < (const GameState& other) const { return gameBoard < other.gameBoard; }
    
    // And overwrite equal operator for easier evaluation
    bool operator == (const GameState& other) const { return gameBoard == other.gameBoard; }
};

// --------------------------------------------------------------------
// Create a new GameState with the EmptyField shifted to a new position
GameState GameState::swapPosition(const Direction& direction){

    // Make a copy from this gameBoard for a new GameState
    GameState swappedGameState = *this;
    
    // Find the EmptyField
    for (int row{}; row < static_cast<const int>(NumberOfRows);   row) {
        for (int column{}; column < static_cast<const int>(NumberOfColumns);   column) {
            if (gameBoard[row][column] == EmptyField) {
                
                // Calculate new row and column index based on the given direction
                // DO not cross the borders of the gameBoard
                const int nextRow = std::clamp((row direction.first), BorderUp, BorderBottom);
                const int nextColumn = std::clamp((column direction.second), BorderLeft, BorderRight);
                
                // Shift. Exchange the EmptyField with a char in the given direction
                std::swap(swappedGameState.gameBoard[row][column],swappedGameState.gameBoard[nextRow][nextColumn]);
            }
        }
    }
    return swappedGameState;
}


// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
int main(int argc, char **argv) {
    
    // First check, if we have sufficient command line arguments
    if (argc == 3) {
        
        // OK, we have command line arguments, try to open the specified files
        if (std::ifstream inputStream{argv[1]}; inputStream) {

            // Now the output stream
            if (std::ofstream outputStream{argv[2]}; outputStream) {
                
                // All files are open. Get start and destination game states
                // First define the instances
                GameState startGameState{};
                GameState destinationGameState{};

                // Then read/extract data from file and store in instances
                inputStream >> startGameState;
                inputStream >> destinationGameState;
                
                // Set/Initialize evaluation parameters
                // Here we will store the number of swaps that lead to this state
                std::map<GameState, unsigned int> numberOfSwaps{{startGameState, 0u}};

                // Here we will store all intermediate states after one or many swaps
                std::queue<GameState> trialGameState{};
                trialGameState.push(startGameState);
                
                // Indicator, if we found a solution or not
                bool solutionFound{};
                
                // Until we find a solution (or not)
                while (not trialGameState.empty() && not solutionFound) {
                    
                    // get the current (latest swapped) game state
                    GameState currentGameState = trialGameState.front();
                    trialGameState.pop();
                    
                    // check for end condition. Did we find the desitnation combination
                    if (currentGameState == destinationGameState) {
                        solutionFound = true;
                        outputStream << numberOfSwaps[currentGameState] << std::endl; 
                    }
                    else { 
                        
                        // Get next states in all directions. Iterate over all possible moves
                        for (const Direction& direction : Moves) {
                            
                            // Next state for this direction
                            GameState nextState = currentGameState.swapPosition(direction);
                            
                            // Prevent circular swapping and storing double elements
                            if (numberOfSwaps.count(nextState) == 0) {
                                
                                // OK, valid next state. Store the number of swaps done
                               numberOfSwaps[nextState] = numberOfSwaps[currentGameState]   1 ;
                               // And save this state as one potential new candidate 
                               trialGameState.push(nextState);
                            }
                        }
                    }
                }  // All error mesaages
                if (not solutionFound)
                    std::cerr << "\nNo solution found\n\n"; 
            }
            else std::cerr << "\nError: Could not open input file '" << argv[1] << "'\n"; 
        }
        else std::cerr << "\nError: Could not open output file '" << argv[2] << "'\n"; 
    }
    else std::cerr << "\nError: Please call with: app inputFileName outputFileName\n"; 
    return 0;
}

But even this needs to be further improved. At lease an input validation should be addedd. Anyway, have fun.

CodePudding user response:

In C read characters from input file like this :

#include <fstream>

char input_c;
std::ifstream input_file("input.txt");
while (input_file >> input_c) // stops at eof
{
    // validate input
    //.. do your things..
}
  • Related