Основы языка Kotlin
Теоретические сведения
Цель работы
Освоить основные конструкции языка Kotlin для структурного программирования, среду разработки, методы тестирования и отладки программ.
Задание
При выполнении этой работы нужно реализовать консольную игру «Крестики-нолики» на доске 3*3.
Среда разработки
-
Установите и настройте на своем компьютере среду разработки.
-
Напишите функцию для вычисления площади круга по его радиусу. Параметр и результат функции должны быть типа
Double
, константуPI
можно импортировать из библиотекиkotlin.math
. -
Напишите тесты для этой функции. Для корректного сравнения вещественных чисел задайте с помощью функции
plusOrMinus
точность сравнения (например, для радиуса1
результат должен бытьPI.plusOrMinus(0.001)
) -
Переименуйте функцию с помощью рефакторинга. Отформатируйте текст с помощью меню среды
Code-Reformat Code
. Запомните сочетания клавиш для выполненных действий. -
Установите точку остановки вначале функции тестирования, запустите программу в режиме отладки. Проверьте как работают в окне отладки кнопки «Step over», «Step into», «Rerun», «Resume», «Stop». Добавьте в код функции произвольную переменную, затем просмотрите значение этой переменной в режиме отладки и добавьте эту переменную для просмотра с помощью поля «Evaluate expression». Запомните сочетания клавиш для выполненных действий.
-
Реализуйте функцию выводящую в выходной поток
out
игровое поле (размер игрового поля задается с помощью глобальной переменнойvar boardSize = 3
):fun printBoard(board: Array<Array<Char>>, out: PrintStream = outputConsole)
Для тестирования функции можно использовать следующий код:
"print" { val board = arrayOf( arrayOf(' ',' ',' '), arrayOf(' ','X',' '), arrayOf(' ',' ','0')) printBoard(board, output) outputBuffer.toString() shouldBe " 012\n0 \n1 X \n2 0\n" }
Напишите тест функции
printBoard
для случая другого размера доски.
Реализации игры
При реализации игры нужно реализовать несколько функций. Каждая функция должны быть протестирована.
-
Реализуйте функцию проверки состояния игровой доски на наличие на ней выигрышной комбинации (трех одинаковых символов в линию):
fun checkWin(board: Array<Array<Char>>): Char
Для упрощение реализации этой функции все выигрышные комбинации можно задать с помощью массива:
val winLines = arrayOf( arrayOf(arrayOf(0, 0), arrayOf(0, 1), arrayOf(0, 2)), arrayOf(arrayOf(1, 0), arrayOf(1, 1), arrayOf(1, 2)), arrayOf(arrayOf(2, 0), arrayOf(2, 1), arrayOf(2, 2)), arrayOf(arrayOf(0, 0), arrayOf(1, 0), arrayOf(2, 0)), arrayOf(arrayOf(0, 1), arrayOf(1, 1), arrayOf(2, 1)), arrayOf(arrayOf(0, 2), arrayOf(1, 2), arrayOf(2, 2)), arrayOf(arrayOf(0, 0), arrayOf(1, 1), arrayOf(2, 2)), arrayOf(arrayOf(0, 2), arrayOf(1, 1), arrayOf(2, 0)) )
Внутри функции должен быть цикл, перебирающий все элементы массива
winLines
. Для каждого элемента массива (назовем его линия) выполняется проверка: если символ доски с координатами, указанными в первом элементе линии, не равен пробелу и равен символам с координатами во втором и в третьем элементах линии, то это выигрышная комбинации и функция возвращает этот символ. Если после перебора всех линий выигрышная комбинация не найдена, то функция должна вернуть символ пробела. -
Реализуйте функцию, которая проверяет, имеются ли на доске пустые поля:
fun isFill(board: Array<Array<Char>>): Boolean
-
Реализуйте функцию для преобразования введенной пользователем с клавиатуры строки в пару чисел, соответствующую координатам на доске:
fun pointFromString(string: String): Array<Int>
Для разделения строки на отдельные элементы и преобразования ее в массив слов используйте функцию
split
с разделителем равным пробелу.Если пользователь ввел не правильную строку, то при преобразовании строки в число может возникнуть ошибка. Обработка ошибок будет рассмотрена в курсе позже, а пока можно воспользоваться следующим примером:
val x = arr[0].toIntOrNull() ?: return arrayOf(0)
Здесь
arr[0]
элемент массива строк,toIntOrNull
– функция, преобразующая строку в число и возвращающая в случае ошибки специальное значение (null
),?:
– оператор, второй операнд которого срабатывает, если первый операнд равенnull
,arrayOf(0)
– значение, которая наша функция возвращает, если преобразование строки в координаты прошло неуспешно. -
Реализуйте функцию для проверки возможности хода в заданное поле доски:
fun isRightMove(board: Array<Array<Char>>, point: Array<Int>): Boolean
Функция должна проверить, что координаты
point
не выходят за пределы доски (используйте функцию`in`
) и что соответствующее поле доски пустое. -
Реализуйте основную функцию игры:
fun game(inputStream: InputStream= System.`in`, output: PrintStream = outputConsole)
Функция должна содержать внешний цикл
while
, который выполняется пока на доске нет выигрышной комбинации, есть свободные поля и пользователь вводит корректные координаты. В цикле пользователь вводит координаты, на поле доски выставляется текущий символ (X
или0
), текущий символ меняется на противоположный, доска печатается.Координаты пользователь вводит во внутреннем цикле типа
do {...} while()
. Цикл продолжается пока пользователь вводит два числа – координаты, но по этим координатам нельзя сделать правильный ход. Если пользователь вводит что-то отличное от двух координат, то внутренний цикл, а затем и внешний, заканчиваются.Функция
game
должна протестирована на правильное завершение игры в случае победы одного из игроков; на правильное завершение игры в ничью; на правильную отработку хода, сделанного за пределы доски и на правильную отработку хода, сделанного в уже занятую клетку.Для экономии времени ниже приведены первые две проверки. Они будут использоваться всех лабораторных работ, отдельные строки в этой проверки нужно будет подключить (установив переменную
lab3=true
) только для третьей и последующих работ.class TestXO : StringSpec({ val stepsWinX = "1 1\n1 2\n0 1\n0 2\n2 1\n" fun resultWinX(lab3: Boolean = false) = (if (lab3) "Ход 0\n" else "") + " 012\n" + "0 \n" + "1 \n" + "2 \n" + (if (lab3) " Очередь хода X\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 1\n" else "") + " 012\n" + "0 \n" + "1 X \n" + "2 \n" + (if (lab3) " Очередь хода 0\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 2\n" else "") + " 012\n" + "0 \n" + "1 X0\n" + "2 \n" + (if (lab3) " Очередь хода X\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 3\n" else "") + " 012\n" + "0 X \n" + "1 X0\n" + "2 \n" + (if (lab3) " Очередь хода 0\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 4\n" else "") + " 012\n" + "0 X0\n" + "1 X0\n" + "2 \n" + (if (lab3) " Очередь хода X\n" else "") + "Ваш ход или команда\n" + if (lab3) "Победил X" else " 012\n" + "0 X0\n" + "1 X0\n" + "2 X \n" val stepsDraw = "0 0\n0 1\n0 2\n1 1\n1 0\n1 2\n2 2\n2 0\n2 1\n" fun resultDraw(lab3: Boolean = false) = (if (lab3) "Ход 0\n" else "") + " 012\n" + "0 \n" + "1 \n" + "2 \n" + (if (lab3) " Очередь хода X\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 1\n" else "") + " 012\n" + "0 X \n" + "1 \n" + "2 \n" + (if (lab3) " Очередь хода 0\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 2\n" else "") + " 012\n" + "0 X0 \n" + "1 \n" + "2 \n" + (if (lab3) " Очередь хода X\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 3\n" else "") + " 012\n" + "0 X0X\n" + "1 \n" + "2 \n" + (if (lab3) " Очередь хода 0\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 4\n" else "") + " 012\n" + "0 X0X\n" + "1 0 \n" + "2 \n" + (if (lab3) " Очередь хода X\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 5\n" else "") + " 012\n" + "0 X0X\n" + "1 X0 \n" + "2 \n" + (if (lab3) " Очередь хода 0\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 6\n" else "") + " 012\n" + "0 X0X\n" + "1 X00\n" + "2 \n" + (if (lab3) " Очередь хода X\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 7\n" else "") + " 012\n" + "0 X0X\n" + "1 X00\n" + "2 X\n" + (if (lab3) " Очередь хода 0\n" else "") + "Ваш ход или команда\n" + (if (lab3) "Ход 8\n" else "") + " 012\n" + "0 X0X\n" + "1 X00\n" + "2 0 X\n" + (if (lab3) " Очередь хода X\n" else "") + "Ваш ход или команда\n" + if (lab3) "Ничья" else " 012\n" + "0 X0X\n" + "1 X00\n" + "2 0XX\n" // Другие тесты "Тест крестиков-ноликов" { listOf( stepsWinX to resultWinX(), stepsDraw to resultDraw(), stepsOutBoard to resultOutBoard(), stepsNoEmpty to resultNoEmpty() ).forEach { outputBuffer.reset() lab1.game(ByteArrayInputStream(it.first.toByteArray()), output) outputBuffer.toString() shouldBe it.second } } })