Я новичок в C++, и у меня есть проект, в котором я должен создать код, который понимает список команд, перечисленных в файле. Мой код успешно открывает файл, но я не уверен, следует ли он командам.
Я составил этот код, но я не уверен, что интерпретация моей команды неверна или мой вывод испорчен. Я продолжаю получать линию «разрезов» вместо дизайна.
подсказка выглядит следующим образом:
Станок с ЧПУ (ЧПУ) используется для создания металлических вывесок с помощью инструмента для лазерной резки.
Лазер находится в одном из двух состояний: включен или выключен.
Когда лазер включен, он режет металл и делает необходимый знак при движении.
Когда лазер выключен, лазер свободно перемещается, ничего не разрезая на куске металла.
Для этого задания вы напишете программу на C++ для управления движением станка с ЧПУ и моделирования того, что он вырезал бы в металлическом прямоугольнике.
Таблица 1: Список команд и их значение.
Command Meaning
I In (move the laser inward so that it begins cutting)
O Out (move the laser outward so that it stops cutting)
R Turn right
L Turn left
A n Move ahead n spaces, n is a positive int <= 80
Пример (ввод пользователя подчеркнут для наглядности, подчеркнутый текст в вашей программе не нужен):
Enter the file name: laser.txt
Generated grid:
* * * * *
*
*
*
*
*
Здесь Laser.txt содержал следующее:
I
A 4
R
A 4
O
A 2
I
Если есть какая-либо недопустимая команда, не указанная в Таблице 1, программа должна быть немедленно остановлена со следующим сообщение об ошибке: «Обнаружена недопустимая команда. Завершение».
Наконец, если указанное имя файла не может быть открыто, выведите следующее сообщение об ошибке: «Ошибка, не удается открыть файл».
Ваша программа должна содержать несколько функций, которые решают более мелкие части проблемы.
Основы сетки
Ниже представлена графическая сетка
x- axis (+)
y
a
x
i
s
+
(0,0)
Это графическое окно. Обратите внимание, что (0,0) — это верхний левый угол, а значения y положительны при движении вниз, а значения x положительны при движении в правильном направлении.
Вы можете предположить, что лазер начинается в положении (0,0) и направлен на восток в соответствии с компасной розой ниже.
Примечание. Вы должны постоянно следить за тем, чтобы лазер оставался в области рисования.
Выше представлена репрезентативная сетка двумерного мира ЧПУ с m=16 и n=16.
T представляет собой начальное положение лазера.
#include <iostream>
#include <cstdlib>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main() {
int n, x = 0, y = 0;
bool cutting = false;
const int gridWidth = 80, gridHeight = 50;
char grid[gridHeight][gridWidth] = {' '};
char command;
string fileName; // obtaining and openning file
cout << "Enter the file name: ";
cin >> fileName;
ifstream file(fileName);
if (!file) {
cout << "Error, cannot open file." << endl;
return 0;
}
while (file >> command) { // using true/false to operate cutting and positioning
if (command == 'I') {
cutting = true;
}
else if (command == 'O') {
cutting = false;
}
else if (command == 'R') {
if (x < gridWidth - 1) x++;
}
else if (command == 'L') {
if (x > 0) x--;
}
else if (command == 'A') {
file >> n;
if (n > 0 && n <= gridWidth - x) {
for (int i = 0; i < n; i++) {
if (cutting) {
grid[y][x+i] = '#';
}
}
x += n;
}
}
}
file.close();
for (int i = 0; i < gridHeight; i++) { // printing grid
for (int j = 0; j < gridWidth; j++) {
cout << grid[i][j];
}
cout << "\n";
}
return 0;
}
Это ваша интерпретация неверна. Лазер имеет направление движения. Так что R
не означает шаг вправо, это означает поворот лазера вправо на 90 градусов. Команда A
перемещает лазер в его текущем направлении движения. В вашем коде лазер шагает влево и вправо, но в остальном всегда движется вниз.
Вы приложили разумные усилия, чтобы объяснить свою проблему, хотя это много текста для чтения. Что на самом деле нужно, так это код (минимальный воспроизводимый пример Я думаю, что он у нас есть), пример ввода (он тоже есть), вывод и ожидаемый вывод (не могу найти). Все, что сверху, хорошо и помогает, но без простого утверждения об ожидаемом результате трудно помочь.
Минимальное количество вещей, которые вам нужно сделать, чтобы заставить его работать: onlinegdb.com/_5mfi36hB
Хорошая задача. И хорошо описано.
С точки зрения программирования, относительно алгоритмов и навыков программа имеет низкую сложность. Усилия по программированию высоки.
Но описание дает много подробных требований и несколько хороших советов.
В программировании вы теперь анализируете требования, а затем разрабатываете дизайн. После этого бы внедрить и протестировать все.
Один из самых важных советов в описании — использовать несколько функций для решения небольших частей задачи. Это очень важное правило, которое поможет вам всегда. Разбейте большую проблему на более мелкие. Решение для них будет намного проще.
Давайте посмотрим, какие более мелкие строительные блоки мы могли бы использовать.
Очень важное требование: всегда оставайтесь в рамках матрицы.
Теперь мы шаг за шагом реализуем требования.
Определение матрицы. Требования говорят, используйте массив 2d с размерами 10x50. Размерность является важным параметром, поэтому мы можем определить ее через глобальные константы. В C++ мы используем для этого константы времени компиляции, которые определяются с помощью ключевого слова constexpr
. Кроме того, у вас может возникнуть соблазн использовать массивы C-Style, такие как char matrix[10][50]
. Но лучше использовать std::array
из библиотеки контейнеров (это похоже на std::vector
, но его размер статичен. Чтобы больше абстрагироваться и облегчить понимание, мы будем использовать операторы using
или typedef
для создания псевдонимов.
// Define the size of the matrix
constexpr int NumberOfRows = 10;
constexpr int NumberOfColumns = 50;
// This will be our 2d matrix
using Matrix = std::array<std::array<char, NumberOfColumns>, NumberOfRows>;
// Note , you could also write
// using Matrix = char[NumberOfRows][NumberOfColumns];
Теперь нам нужно что-то, чтобы инициализировать матрицу или заполнить ее значением. Требование говорит, заполните его 0es.
Это можно сделать с помощью простого двойного вложенного цикла for
. Мы создаем функцию, в которой мы можем выбрать «символ заполнения», но присвоить ему значение по умолчанию 0, что означает «пустой». Функция может быть реализована следующим образом:
// Indicator for a not processed field
constexpr char ConditionSolid = '\0';
constexpr char ConditionCut = '\1';
// This will set all values in the matrix
void fillMatrix(Matrix& matrix, const char fillChar = ConditionSolid) {
for (int row = 0; row < NumberOfRows; ++row) {
for (int column = 0; column < NumberOfColumns; ++column) {
matrix[row][column] = fillChar;
}
}
}
Следующая функциональность, которая нам нужна, — это чтение команд из файла. Нам нужно открыть файл и проверить, можно ли его открыть. Затем нам нужно прочитать строку за строкой и сохранить результаты. Так как мы не знаем, сколько строк в файле, нам нужен контейнер, который может динамически расти, поэтому std::vector
и так как команды имеют разную и возможно неизвестную длину, мы будем использовать одну команду как строку. Все это мы снова абстрагируем объявлением «using». Поскольку функция может завершиться ошибкой, мы вернем логическое значение с true
в качестве успеха. Что-то вроде приведенного ниже:
//-------------------------------------------------------------------------------
// Read commands from file
using CommandLine = std::string;
using CommandList = std::vector<CommandLine>;
bool readCommandsFromFile(const std::string& fileName, CommandList& commandList) {
// Function return value. True is OK, false means error. We assume error in the beginning
bool result = false;
// Open the file.
std::ifstream ifs(fileName);
// Check, if the file could be opened
if (ifs) {
// Ok, now the file is open, we want to read command lines
CommandLine commandLine{};
// Read all lines in a loop, until the all data have been read
while (std::getline(ifs, commandLine)) {
// Store new, just read line in the command list
commandList.push_back(commandLine);
}
result = true;
}
else {
// The file could not be opened or any other stream error
std::cout << "Error, cannot open file.";
}
return result;
}
Итак, теперь у нас есть список с командными строками. Эти команды должны быть позже извлечены из строки и разделены на команду с потенциальным атрибутом. Поскольку в командной строке могут быть недопустимые команды, мы проверим это, а также вернем логическое значение false
в случае возникновения проблемы.
Эта функция имеет небольшую длину из-за множества проверок ошибок.
// Define List with commands (we directly use the define Letters for that)
using Command = char;
using CommandAttribute = int;
constexpr Command CMD_In = 'I';
constexpr Command CMD_Out = 'O';
constexpr Command CMD_TurnRight = 'R';
constexpr Command CMD_TurnLeft = 'L';
constexpr Command CMD_Move = 'A';
bool getCommand(const CommandLine& commandLine, Command& command, CommandAttribute& commandAttribute) {
// Function return value. True is OK, false means error. We assume OK in the beginning
bool result = true;
// Sanity check
if (commandLine.length() > 0) {
// check first letter in cammand line
switch (commandLine[0]) {
case CMD_In: // Fallthrough
case CMD_Out: // Fallthrough
case CMD_TurnRight: // Fallthrough
case CMD_TurnLeft:
command = commandLine[0];
commandAttribute = 0;
break;
case CMD_Move:
// The move command has a parameter, This we need to convert to an integer
// First we need to check, if a valid integer is given
// Integer starts at index 2 we allow only for a small number of integers
bool validInteger = ((commandLine.length()>1) and (commandLine.length()<6));
for (int i = 2; i < commandLine.length() and validInteger; ++i) {
if (not std::isdigit(commandLine[i])) {
validInteger = false;
}
}
// Ok there was a valid character sequence forming an integer
if (validInteger) {
command = commandLine[0];
commandAttribute = std::stoi(commandLine.substr(1)));
}
else {
// Some wrong data in the command
command = '\0';
commandAttribute = 0;
result = false;
}
break;
default:
// Unknwon command. Result of function will be false
command = '\0';
commandAttribute = 0;
result = false;
}
}
return result;
}
Теперь нам нужно выполнить команды. Мы начинаем с позиции x и y 0, а затем читаем команды. Выполняем их поэтапно.
Важно то, что мы не пересекаем границы. Движение будет выполняться путем добавления или вычитания значений в зависимости от текущего направления.
Для направления мы используем целое число. Каждый раз, когда есть «правильная» команда, мы увеличиваем значение. С делением по модулю 4 мы избегаем переполнения. Для «Left» мы уменьшаем на единицу. Отрицательные значения будут компенсированы добавлением 4. На самом деле мы всегда будем добавлять 4, а затем использовать деление по модулю для ограничения значения.
К сожалению, это довольно длинно
// Execute the commands
using Direction = int;
using LaserState = int;
constexpr Direction DirectionNorth = 0;
constexpr Direction DirectionEast = 1;
constexpr Direction DirectionSouth = 2;
constexpr Direction DirectionWest = 3;
constexpr LaserState LaserStateOff = 0;
constexpr LaserState LaserStateOn = 1;
bool execute(const CommandList& commandList, Matrix& matrix) {
// Function return value. True is OK, false means error. We assume OK in the beginning
bool result = true;
// Set initial values
int currentX = 0;
int currentY = 0;
Direction currentdirection = DirectionEast;
Command command{};
CommandAttribute commandAttribute{};
LaserState laserState = LaserStateOff;
// Execute all commands
for (const CommandLine& commandLine : commandList) {
// First, get the command
if (getCommand(commandLine, command, commandAttribute)) {
switch (command) {
case CMD_In:
laserState = LaserStateOn;
matrix[currentY][currentX] = ConditionCut;
break;
case CMD_Out:
laserState = LaserStateOff;
break;
case CMD_TurnRight:
++currentdirection;
currentdirection = currentdirection % 4;
break;
case CMD_TurnLeft:
--currentdirection;
currentdirection = (currentdirection + 4) % 4;
break;
case CMD_Move:
// This is a longer story and should probably be put in a function as well
{
// Check Laserstate and correspong fillchar
char fillChar = ConditionSolid;
if (laserState == LaserStateOn) {
fillChar = ConditionCut;
}
int endPosition{};
switch (currentdirection) {
case DirectionNorth:
// Calculate new target position
endPosition = currentY - commandAttribute;
// Limit to border
if (endPosition < 0) endPosition = 0;
// Set values
for (int i = currentY; (laserState == LaserStateOn) and i >= endPosition; --i)
matrix[i][currentX] = fillChar;
currentY = endPosition;
break;
case DirectionSouth:
// Calculate new target position
endPosition = currentY + commandAttribute;
// Limit to border
if (endPosition >= NumberOfRows) endPosition = NumberOfRows-1;
// Set values
for (int i = currentY; (laserState == LaserStateOn) and i <= endPosition; ++i)
matrix[i][currentX] = fillChar;
currentY = endPosition;
break;
case DirectionWest:
// Calculate new target position
endPosition = currentX - commandAttribute;
// Limit to border
if (endPosition < 0) endPosition = 0;
// Set values
for (int i = currentX; (laserState == LaserStateOn) and i >= endPosition; --i)
matrix[currentY][i] = fillChar;
currentX = endPosition;
break;
case DirectionEast:
// Calculate new target position
endPosition = currentX + commandAttribute;
// Limit to border
if (endPosition >= NumberOfColumns) endPosition = NumberOfColumns - 1;
// Set values
for (int i = currentX; (laserState == LaserStateOn) and i <= endPosition; ++i)
matrix[currentY][i] = fillChar;
currentX = endPosition;
break;
}
}
break;
default:
result = false;
std::cout << "Internal programming error.";
break;
}
}
else {
// Illegal command found
result = false;
std::cout << "Encountered an Invalid command. Terminating.";
break;
}
}
return result;
}
Функция печати довольно проста. Не нужно показывать это здесь.
Тогда функция main будет довольно короткой. Затем мы можем объединить все вместе:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <array>
//-------------------------------------------------------------------------------
// Define the size of the matrix
constexpr int NumberOfRows = 10;
constexpr int NumberOfColumns = 50;
// This will be our 2d matrix
using Matrix = std::array<std::array<char, NumberOfColumns>, NumberOfRows>;
// Note , you could also write
// using Matrix = char[NumberOfRows][NumberOfColumns];
//-------------------------------------------------------------------------------
// Indicator for a not processed field
constexpr char ConditionSolid = '\0';
constexpr char ConditionCut = '\1';
// This will set all values in the matrix
void fillMatrix(Matrix& matrix, const char fillChar = ConditionSolid) {
for (int row = 0; row < NumberOfRows; ++row) {
for (int column = 0; column < NumberOfColumns; ++column) {
matrix[row][column] = fillChar;
}
}
}
//-------------------------------------------------------------------------------
// Read commands from file
using CommandLine = std::string;
using CommandList = std::vector<CommandLine>;
bool readCommandsFromFile(const std::string& fileName, CommandList& commandList) {
// Function return value. True is OK, false means error. We assume error in the beginning
bool result = false;
// Open the file.
std::ifstream ifs(fileName);
// Check, if the file could be opened
if (ifs) {
// Ok, now the file is open, we want to read command lines
CommandLine commandLine{};
// Read all lines in a loop, until the all data have been read
while (std::getline(ifs, commandLine)) {
// Store new, just read line in the command list
commandList.push_back(commandLine);
}
result = true;
}
else {
// The file could not be opened or any other stream error
std::cout << "Error, cannot open file.";
}
return result;
}
//-------------------------------------------------------------------------------
// Define List with commands (we directly use the define Letters for that)
using Command = char;
using CommandAttribute = int;
constexpr Command CMD_In = 'I';
constexpr Command CMD_Out = 'O';
constexpr Command CMD_TurnRight = 'R';
constexpr Command CMD_TurnLeft = 'L';
constexpr Command CMD_Move = 'A';
bool getCommand(const CommandLine& commandLine, Command& command, CommandAttribute& commandAttribute) {
// Function return value. True is OK, false means error. We assume OK in the beginning
bool result = true;
// Sanity check
if (commandLine.length() > 0) {
// check first letter in cammand line
switch (commandLine[0]) {
case CMD_In: // Fallthrough
case CMD_Out: // Fallthrough
case CMD_TurnRight: // Fallthrough
case CMD_TurnLeft:
command = commandLine[0];
commandAttribute = 0;
break;
case CMD_Move: {
// The move command has a parameter, This we need to convert to an integer
// First we need to check, if a valid integer is given
// Integer starts at index 2 we allow only for a small number of integers
bool validInteger = ((commandLine.length() > 1) and (commandLine.length() < 6));
for (int i = 2; i < commandLine.length() and validInteger; ++i) {
if (not std::isdigit(commandLine[i])) {
validInteger = false;
}
}
// Ok there was a valid character sequence forming an integer
if (validInteger) {
command = commandLine[0];
commandAttribute = std::stoi(commandLine.substr(1));
}
else {
// Some wrong data in the command
command = '\0';
commandAttribute = 0;
result = false;
}}
break;
default:
// Unknwon command. Result of function will be false
command = '\0';
commandAttribute = 0;
result = false;
break;
}
}
return result;
}
//-------------------------------------------------------------------------------
// Execute the commands
using Direction = int;
using LaserState = int;
constexpr Direction DirectionNorth = 0;
constexpr Direction DirectionEast = 1;
constexpr Direction DirectionSouth = 2;
constexpr Direction DirectionWest = 3;
constexpr LaserState LaserStateOff = 0;
constexpr LaserState LaserStateOn = 1;
bool execute(const CommandList& commandList, Matrix& matrix) {
// Function return value. True is OK, false means error. We assume OK in the beginning
bool result = true;
// Set initial values
int currentX = 0;
int currentY = 0;
Direction currentdirection = DirectionEast;
Command command{};
CommandAttribute commandAttribute{};
LaserState laserState = LaserStateOff;
// Execute all commands
for (const CommandLine& commandLine : commandList) {
// First, get the command
if (getCommand(commandLine, command, commandAttribute)) {
switch (command) {
case CMD_In:
laserState = LaserStateOn;
matrix[currentY][currentX] = ConditionCut;
break;
case CMD_Out:
laserState = LaserStateOff;
break;
case CMD_TurnRight:
++currentdirection;
currentdirection = currentdirection % 4;
break;
case CMD_TurnLeft:
--currentdirection;
currentdirection = (currentdirection + 4) % 4;
break;
case CMD_Move:
// This is a longer story and should probably be put in a function as well
{
// Check Laserstate and correspong fillchar
char fillChar = ConditionSolid;
if (laserState == LaserStateOn) {
fillChar = ConditionCut;
}
int endPosition{};
switch (currentdirection) {
case DirectionNorth:
// Calculate new target position
endPosition = currentY - commandAttribute;
// Limit to border
if (endPosition < 0) endPosition = 0;
// Set values
for (int i = currentY; (laserState == LaserStateOn) and i >= endPosition; --i)
matrix[i][currentX] = fillChar;
currentY = endPosition;
break;
case DirectionSouth:
// Calculate new target position
endPosition = currentY + commandAttribute;
// Limit to border
if (endPosition >= NumberOfRows) endPosition = NumberOfRows-1;
// Set values
for (int i = currentY; (laserState == LaserStateOn) and i <= endPosition; ++i)
matrix[i][currentX] = fillChar;
currentY = endPosition;
break;
case DirectionWest:
// Calculate new target position
endPosition = currentX - commandAttribute;
// Limit to border
if (endPosition < 0) endPosition = 0;
// Set values
for (int i = currentX; (laserState == LaserStateOn) and i >= endPosition; --i)
matrix[currentY][i] = fillChar;
currentX = endPosition;
break;
case DirectionEast:
// Calculate new target position
endPosition = currentX + commandAttribute;
// Limit to border
if (endPosition >= NumberOfColumns) endPosition = NumberOfColumns - 1;
// Set values
for (int i = currentX; (laserState == LaserStateOn) and i <= endPosition; ++i)
matrix[currentY][i] = fillChar;
currentX = endPosition;
break;
}
}
break;
default:
result = false;
std::cout << "Internal programming error.";
break;
}
}
else {
// Illegal command found
result = false;
std::cout << "Encountered an Invalid command. Terminating.";
break;
}
}
return result;
}
//-------------------------------------------------------------------------------
// printing the matrix
void print(const Matrix& matrix) {
std::cout << "\n\n";
for (int row = 0; row < NumberOfRows; ++row) {
for (int column = 0; column < NumberOfColumns; ++column) {
std::cout << ((matrix[row][column] == ConditionCut) ? '*' : ' ');
}
std::cout << '\n';
}
}
int main() {
Matrix matrix{};
fillMatrix(matrix);
CommandList commandList{};
const std::string commandFileName = "r:\\laser.txt";
if (readCommandsFromFile(commandFileName, commandList)) {
if (execute(commandList, matrix)) {
print(matrix);
}
}
}
Добро пожаловать на stackoverflow.com. Пожалуйста, найдите время, чтобы прочитать страницы справки , особенно разделы под названием "На какие темы я могу задать здесь вопросы?" и «Каких вопросов мне следует избегать?» . Также, пожалуйста, посетите тур и прочитайте о Как спросить . Наконец, пожалуйста, прочитайте этот контрольный список вопросов.