Opened 4 years ago
Closed 4 years ago
#729 closed ожидается проверка (задача сдана)
HW #2
Reported by: | Brilliantov Kirill | Owned by: | Sokolov Viacheslav |
---|---|---|---|
Component: | HW #2 (X0) | Version: | 2.0 |
Keywords: | Cc: |
Description
Change History (5)
comment:2 Changed 4 years ago by
Type: | ожидается проверка → ожидаются исправления |
---|
Просьба сделать так, чтобы стандартный поток вывода в точности соответствовал тому, как описано в условии.
В частности:
- первыми ходят O
- перевод строки перед выводом поля
- Bad move в std::cout
- нет приглашения поиграть еще раз
Makefile:
зачем нужна цель kek?
зачем нужен OBJECTS_APP_WO_MAIN?
WINNING_NUMBER не очень понятное именование ("выигрывающее число")
Player:
В целом можно оставить как есть, но хочу акцентировать внимание вот на чем:
Используется в двух разных ипостасях (очередность игрока) и содержимое клетки. Между собой эти сущности связаны, но отличаются. NONE в качестве текущего игрока означает, что игра окончена и никто походить не может. NONE в качестве варианта хода не имеет смысла (и не должен быть допустим при вызове из интерфейса приложения).
То есть можно было бы разбить сущность на две:
CellContent?? (какой символ записан в клетке; X / O / Empty)
Player (игрок, X / O)
currentPlayer бы возращал std::optional<Player>
canMove / move принимал бы Player
Отличие между типами могло бы уберечь от ошибок - на этапе компиляции могла бы быть проверка, что программист подумал о разнице между содержимым клетки и очередностью хода и имеет в виду именно очередность хода при вызове метода. Больше кода, зато меньше возможностей ошибиться и проще модифицировать (вдруг Вы захотите добавить препятствия на игровое поле?)
39 void from_string(const std::string &str);
лучше сделать конструктор от field_t, а функцию сделать внешней (прямо в тесте завести вспомогательную функцию), тогда решается проблема непонятных гарантий
clear_cell_content -> reset?
isRow - не очень понятное именование. Неочевидно, что эта функция делает и связана ли как-то с целью игры.
4 #include "Board.h"
5 #include "BoardView?.h"
6 #include "NCursesBoardView.h"
7 #include "StdioBoardView?.h"
все эти включения не нужны в include/BoardController.h, достаточно forward declaration на Board и BoardView?
19 bool isExit() const;
непонятно, что должна делать эта функция (что означает возвращаемое значение)
15 virtual bool getMove(Position &pos) = 0;
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-out
лучше сделать virtual std::optional<Position> tryGetMove() = 0;
5 #include "ncurses.h"
не нужно в include/NCursesBoardView.h
9 Position(int row = 0, int col = 0);
1) лучше пометить как explicit - сейчас допустима implicit конвертация из NULL (например)
2) в принципе есть смысл делать такие структуры, если хочется гарантировать иммутабельность, например, но в данном случае выглядит более оправданным
struct Position {int row; int column;}
3) isInRange явно не должна принадлежать Position, потому что сейчас этот класс оказывается связанным с Board. Если хочется этой связанности, то лучше связать их на уровне пространства имен, например, положить Position внутрь Board
8 #define CHECK(expr) \
9 Test::check((expr), PRETTY_FUNCTION, FILE, LINE); \
10 if (!(expr)) \
11 return;
1) лучше поместить внутрь scope {}
2) сейчас на expr нет никаких ограничений - например, это может быть функция, которая меняет состояние обекта, что-нибудь вроде priority_queue.pop(). Из-за этого повторная подставонка expr может приводить к нежелаемому в месте вызова результату. Лучше сделать более явно:
{
const bool result = expr;
check(result,...);
if (!result) return;
}
6 constexpr size_t _size_buffer = (FIELD_SIZE + 1) * (FIELD_SIZE + 1) + 5;
зачем-то нужно?
ALL TESTS ARE PASSED -> ALL TESTS PASSED
2 #include "TestRegister?.h"
не нужен в test/Test.cpp?
4 #define _THIS (*this)
лучше для аналогичных макросов делать #undef, чтобы он случайно куда-то еще не просочился. Так может быть при unity build, например.
А вообще выигрышь от макроса сомнительный.
Board::canMakeMove
игра может быть уже окончена?
bool Board::makeMove
непонятно, почему выбран такой интерфейс, а не более прямолинейный void makeMove (с assert-ом, что его можно сделать)
72 assert(-2 < dpos.getRow() && dpos.getRow() < 2);
73 assert(-2 < dpos.getCol() && dpos.getCol() < 2);
то есть проверяется, что попадает в квадарт от {{-1,-1}, {1,1}} - для этого можно было бы сделать метод (он уже есть на самом деле, но завязан на Board)
runGame
код трудно воспринимать, много if-else. Попробуйте упростить, можно сделать более прямолинейным.
ncurses
45 printw("Do you want to play again?(Y/n)\n");
сообщение сбивает с толку, потому что любая клавиша трактуется как n
можно поддержать и WASD, и стрелочки; про управление можно явно написать в пользовательском интерфейсе
==3820== Memcheck, a memory error detector
==3820== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3820== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==3820== Command: /home/nicesap/HSE/svn/brilliantov.kirill/hw_02/hw_02
==3820== Parent PID: 2817
==3820==
==3820== Conditional jump or move depends on uninitialised value(s)
==3820== at 0x10A572: operator>>(std::istream&, Position&) (Position.cpp:36)
==3820== by 0x10B414: StdioBoardView::getMove(Position&) (StdioBoardView?.cpp:52)
==3820== by 0x10AE96: BoardController::runGame() (BoardController?.cpp:15)
==3820== by 0x109F53: main (main.cpp:18)
==3820==
==3820==
==3820== HEAP SUMMARY:
==3820== in use at exit: 0 bytes in 0 blocks
==3820== total heap usage: 7 allocs, 7 frees, 81,157 bytes allocated
==3820==
==3820== All heap blocks were freed -- no leaks are possible
==3820==
==3820== For counts of detected and suppressed errors, rerun with: -v
==3820== Use --track-origins=yes to see where uninitialised values come from
==3820== ERROR SUMMARY: 3 errors from 1 contexts (suppressed: 0 from 0)
comment:3 Changed 4 years ago by
Отсутствует проверка успешности ввода / вывода
(в смысле badbit/failbit у stdin/stdout)
comment:4 Changed 4 years ago by
Type: | ожидаются исправления → ожидается проверка |
---|---|
Version: | 1.0 → 2.0 |
comment:5 Changed 4 years ago by
Resolution: | → задача сдана |
---|---|
Status: | assigned → closed |
По условию Bad move!
7 enum class Mode : int { NORMAL, SILENT };
слишком общее название для глобального пространства имен
88 bool Board::isInRange(const Position &pos, const Position &leftTopCorner, 89 const Position &rightBottomCorner) { 90 return leftTopCorner.getRow() <= pos.getRow() && 91 leftTopCorner.getCol() <= pos.getCol() && 92 pos.getRow() < rightBottomCorner.getRow() && 93 pos.getCol() < rightBottomCorner.getCol(); 94 }
было бы хорошо отразить асимметричность в названии (Inclusive, Exclusive)
34 is.exceptions(std::ios::failbit | std::ios::badbit);
в таких случаях желательно возращать имевшиеся до этого флаги, чтобы функция была прозрачной в использовании. Чем меньше side effect-ов, тем лучше.