@ Что происходит & @ Время & @ Слайды & @ Описание \\
\hline
\endhead
@ Организационный момент & 5 tag(beg) & @ 1-5 & @ Преподаватель ожидает студентов, поддерживает активность и коммуникацию в чате, озвучивает цели и планы на семинар. Важно упомянуть, что выполнение домашних заданий с лекции является, фактически, подготовкой к семинару \\
\hline
@ Quiz & 5 & @ 6-18 & @ Преподаватель задаёт вопросы викторины, через 30 секунд демонстрирует слайд-подсказку и ожидает ответов (по минуте на ответ) \\
@ Рассмотрение ДЗ лекции & 15-20 & @ 19-23 & @ Преподаватель демонстрирует свой вариант решения домашнего задания с лекции, возможно, по предварительному опросу, демонстрирует и разбирает вариант решения одного из студентов \\
\item\textbf{Цель этапа:} Позитивно начать урок, создать комфортную среду для обучения.
\item\textbf{Тайминг:} 3-5 минут.
\item\textbf{Действия преподавателя:}
\begin{itemize}
\item Запрашивает активность от аудитории в чате;
\item Презентует цели курса и семинара;
\item Презентует краткий план семинара и что студент научится делать.
\end{itemize}
\end{itemize}
\subsubsection*{Quiz}
\begin{itemize}
\item\textbf{Цель этапа:} Вовлечение аудитории в обратную связь.
\item\textbf{Тайминг:} 5--7 минут (4 вопроса, по минуте на ответ).
\item\textbf{Действия преподавателя:}
\begin{itemize}
\item Преподаватель задаёт вопросы викторины, представленные на слайдах презентации;
\item через 30 секунд демонстрирует слайд-подсказку и ожидает ответов.
\end{itemize}
\item\textbf{Вопросы и ответы:}
\begin{enumerate}
\item Свойства окна, такие как размер и заголовок возможно задать (2)
\begin{enumerate}
\item написанием методов в классе-наследнике
\item вызовом методов в конструкторе
\item созданием констант в первых строках класса
\end{enumerate}
\item Для выполнения кода по нажатию кнопки на интерфейсе нужно (1)
\begin{enumerate}
\item создать обработчик кнопки и вписать код в него
\item переопределить метод нажатия у компонента кнопки
\item написать код непосредственно в методе создания кнопки
\end{enumerate}
\item Что такое Java Swing? (1)
\begin{enumerate}
\item набор библиотек для создания реактивных GUI;
\item среда разработки для Java;
\item язык программирования.
\end{enumerate}
\item Какая библиотека используется для создания графических интерфейсов на Java? (3)
\begin{enumerate}
\item Java Swing;
\item JavaFX;
\itemобе библиотеки.
\end{enumerate}
\end{enumerate}
\end{itemize}
\subsubsection*{Рассмотрение ДЗ}
\begin{itemize}
\item\textbf{Цель этапа:} Пояснить не очевидные моменты в формулировке ДЗ с лекции, синхронизировать прочитанный на лекции материал к началу семинара.
\item\textbf{Тайминг:} 15-20 минут.
\item\textbf{Действия преподавателя:}
\begin{itemize}
\item Преподаватель демонстрирует свой вариант решения домашнего задания из лекции;
\item возможно, по предварительному опросу, демонстрирует и разбирает вариант решения одного из студентов.
\end{itemize}
\item\textbf{Домашнее задание из лекции:}
\begin{itemize}
\item Полностью разобраться с кодом -- это задание не нужно обсуждать, возможно просто спросить, кто действительно сел, взял в руки карандаш и блокнот, и попытался разобраться с кодом с лекции. Похвалить тех, кто это действительно сделал.
\item Переделать проверку победы, чтобы она не была реализована просто набором условий.
\textbf{Вариант решения}
На уроке был написан код, который, очевидно, никуда не годится
if (field[0][0]==c && field[0][1]==c && field[0][2]==c) return true;
if (field[1][0]==c && field[1][1]==c && field[1][2]==c) return true;
if (field[2][0]==c && field[2][1]==c && field[2][2]==c) return true;
if (field[0][0]==c && field[1][0]==c && field[2][0]==c) return true;
if (field[0][1]==c && field[1][1]==c && field[2][1]==c) return true;
if (field[0][2]==c && field[1][2]==c && field[2][2]==c) return true;
if (field[0][0]==c && field[1][1]==c && field[2][2]==c) return true;
if (field[0][2]==c && field[1][1]==c && field[2][0]==c) return true;
return false;
}
\end{lstlisting}
Задание подразумевало переписывание изменяющейся индексации в циклы, но, поскольку для игры в крестики-нолики нужно также иметь возможность работать на любом поле с любым числом фигур подряд для победы -- имеет смысл рассматривать сразу следующее задание. Выполнивших это задание отдельно следует похвалить за точность выполнения задания.
\item Попробовать переписать логику проверки победы, чтобы она работала для поля 5х5 и количества фигур 4.
\textbf{Вариант решения}
\href{https://log.iovchinnikov.ru/tic-tac-toe-hint}{более подробное объяснение алгоритма по ссылке}
Задание в первую очередь подразумевало необходимость составить алгоритм действий, прежде, чем писать какой-то код. Важно напомнить студентам о необходимости проектирования перед разработкой. Реализованный в качестве варианта решения алгоритм подразумевает проход по каждой клетке поля с проверкой на наличие линии по четырём направлениям (пошаговая проверка линии реализована методом \code{checkLine}с указанием координат начала проверки, направления и проверяемого символа) с проверкой на выход за пределы поля.
Сначала учимся проверять одну линию в одном направлении. Если начинаем проверку от последнего поставленного символа -- алгоритм сразу становится сложнее, потому что считать надо в 8 сторон, да ещё и учитывать, не был ли последний Х в середине линии, но в итоге такой алгоритм на больших полях будет отрабатывать быстрее. Добавляем -- из каких координат, какой символ, в каком направлении смотрим и проверки на выход за пределы поля (благо поле это глобальные переменные)
Для того, чтобы компьютер мог проверить, может ли он победить и может ли он помешать человеку победить, нужно перед тем как делать ход случайным образом, добавить две проверки, которые должны завершиться выходом из метода хода компьютера, в случае успеха (то есть ход закончен, если мы успешно поставили выигрышную фигуру или успешно предотвратили один из вариантов победы игрока).
Чтобы попытаться выиграть самому компьютер не будет делать ничего сложного -- просто пройдёт по всему полю, попробует поставить последовательно в каждую клетку нолик и проверить, не выигрывает ли он. Если да -- возвращает истину, если не выигрывает -- стирает нолик и продолжает проверку.
Чтобы попытаться предотвратить победу человека -- компьютер действует по тому же принципу -- подставляет в каждую клетку крестик и если на поле победа человека -- заменяет его на свой нолик и возвращает истину, считая, что успешно помешал человеку. Если же победы человека не происходит -- продолжает проверку.
\item\textbf{Ценность этапа} Вовлечение аудитории в обратную связь, пояснение неочевидных моментов в материале лекции и другой проделанной работе.
\item\textbf{Тайминг} 5-15 минут
\item\textbf{Действия преподавателя}
\begin{itemize}
\item Преподаватель ожидает вопросов по теме прошедшей лекции, викторины и продемонстрированной работы;
\item Если преподаватель затрудняется с ответом, необходимо мягко предложить студенту ответить на его вопрос на следующем семинаре (и не забыть найти ответ на вопрос студента!);
\item Предложить и показать пути самостоятельного поиска студентом ответа на заданный вопрос;
\item Посоветовать литературу на тему заданного вопроса;
\item Дополнительно указать на то, что все сведения для выполнения домашнего задания, прохождения викторины и работы на семинаре были рассмотрены в методическом материале к этому или предыдущим урокам.
\end{itemize}
\end{itemize}
\subsubsection*{Задание 1}
\begin{itemize}
\item\textbf{Ценность этапа} Создание окна настроек игры в крестики-нолики, создание и компоновка элементов управления
\item\textbf{Тайминг} 15-20 мин
\item\textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Если группа студентов справилась с заданием, а времени осталось более 5 минут, выдавать группе задания «со звёздочкой».
\end{itemize}
\item\textbf{Задание}:
\begin{itemize}
\itemНа лекции был написан фрейм, содержащий одну кнопку -- начать игру и расположением самого окна настроек автоматически, относительно игрового окна.
Первое задание -- добавить на экран компоновщик-сетку с одним столбцом и добавить над существующей кнопкой следующие компоненты в заданном порядке: \code{JLabel}с заголовком «Выберите режим игры», сгруппированные в \code{ButtonGroup} переключаемые \code{JRadioButton}с указанием режимов «Человек против компьютера» и «Человек против человека», \code{JLabel}с заголовком «Выберите размеры поля», \code{JLabel}с заголовком «Установленный размер поля:», \code{JSlider}со значениями 3..10, \code{JLabel}с заголовком «Выберите длину для победы», \code{JLabel}с заголовком «Установленная длина:», \code{JSlider}со значениями 3..10.
\textbf{Вариант решения}
Решение может быть выполнено сколь угодно гибко. Минимальное жизнеспособное решение приведено в листинге ниже.
JRadioButton pvc = new JRadioButton("Человек против компьютера");
JRadioButton pvp = new JRadioButton("Человек против человека");
bg.add(pvc);
bg.add(pvp);
add(pvc);
add(pvp);
add(new JLabel("Выберите размеры поля"));
add(new JLabel("Установленный размер поля:"));
add(new JSlider(3, 10, 3));
add(new JLabel("Выберите длину для победы"));
add(new JLabel("Установленная длина:"));
add(new JSlider(3, 10, 3));
add(btnStart);
\end{lstlisting}
Если было получено такое решение, важно указать студентам, что мы не сможем снять показания с созданных таким образом компонентов и то, что будет использоваться, например, по кнопке, должно иметь более широкую область видимости, например, объявленным в классе
\item [$*_1$] Сгруппировать объявление компонентов в два метода по смыслу -- регулирование режима игры, регулирование параметров поля. Объявить компоненты так, чтобы они оказались доступны для обработчика кнопки.
\textbf{Вариант решения}
Код ниже. Общий смысл в том, чтобы получить доступные в классе компоненты и сделать конструктор не таким большим. Соответственно, методы должны быть вызваны из конструктора окна.
humVSAI = new JRadioButton("Человек против компьютера");
humVShum = new JRadioButton("Человек против человека");
humVSAI.setSelected(true);
ButtonGroup gameMode = new ButtonGroup();
gameMode.add(humVSAI);
gameMode.add(humVShum);
add(humVSAI);
add(humVShum);
}
private void addFieldControls() {
JLabel lbFieldSize = new JLabel("Установленный размер поля:");
JLabel lbWinLength = new JLabel("Установленная длина:");
slideFieldSize = new JSlider(3, 10, 3);
slideWinLen = new JSlider(3, 10, 3);
add(new JLabel("Выберите размеры поля"));
add(lbFieldSize);
add(slideFieldSize);
add(new JLabel("Выберите длину для победы"));
add(lbWinLength);
add(slideWinLen);
}
\end{lstlisting}
\item [$*_2$] Вынести неизменяемую часть изменяемых сообщений (подписи к слайдерам) в константы класса. Вынести размеры, применяемые в слайдерах в константы класса (избавиться от магических чисел). Вынести обработчик кнопки в отдельный метод.
\textbf{Вариант решения}
В результате должны появиться классовые константы, измениться начало метода \code{addFieldControls} и добавиться метод для обработчика кнопки, все изменения показаны в листинге
private static final String FIELD_SIZE_PREFIX = "Установленный размер поля: ";
private static final String WIN_LENGTH_PREFIX = "Установленная длина: ";
//addFieldControls
private void addFieldControls() {
JLabel lbFieldSize = new JLabel(FIELD_SIZE_PREFIX);
JLabel lbWinLength = new JLabel(WIN_LENGTH_PREFIX);
slideFieldSize = new JSlider(MIN_FIELD_SIZE, MAX_FIELD_SIZE, MIN_FIELD_SIZE);
slideWinLen = new JSlider(MIN_WIN_LENGTH, MAX_FIELD_SIZE, MIN_FIELD_SIZE);
//constructor
private final GameWindow gameWindow;
SettingsWindow(GameWindow gameWindow) {
this.gameWindow = gameWindow;
btnStart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
btnStartDelegate();
}
});
//click delegate
private void btnStartDelegate() {
gameWindow.startNewGame(0, 3, 3, 3);
setVisible(false);
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 2}
\begin{itemize}
\item\textbf{Ценность этапа} Создание окна настроек игры в крестики-нолики, связь компонентов и сбор сведений о состоянии компонента
\item\textbf{Тайминг} 15-20 мин
\item\textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Если группа студентов справилась с заданием, а времени осталось более 5 минут, выдавать группе задания «со звёздочкой».
\end{itemize}
\item\textbf{Задание}:
\begin{itemize}
\item Добавить компонентам интерактивности, а именно, при перемещении ползунка слайдера в соответствующих лейблах должны появляться текущие значения слайдеров. Для этого необходимо добавить к слайдеру слушателя изменений (как это было сделано для действия кнопки).
\textbf{Вариант решения}
Важно не забыть поменять начальные значения у лейблов, чтобы с видимостью экрана сразу становились видимы текущие настройки.
\item [$*_2$] Добавить центрирование окна настроек относительно главного (родительского) окна. То есть, в центре родительского окна должен быть не левый верхний угол окна настроек (как это сделано сейчас), а также его центр.
\textbf{Вариант решения}
В решении важно увидеть отказ от использования метода, который использовался на лекции, потому что при помощи него решить задачу не удастся. Забираем у родительского окна его границы в виде прямоугольника, ищем центр этого прямоугольника по осям и отнимаем от полученных координат половину ширины и высоты окна настроек, соответственно, потому что метод \code{setLocation} будет устанавливать верхний-левый угол окна настроек, а не центр.
int posX = (int) gameWindowBounds.getCenterX() - WINDOW_WIDTH / 2;
int posY = (int) gameWindowBounds.getCenterY() - WINDOW_HEIGHT / 2;
setLocation(posX, posY);
setResizable(false);
//...
}
\end{lstlisting}
\end{itemize}
\end{itemize}
\subsubsection*{Задание 3}
\begin{itemize}
\item\textbf{Ценность этапа} Передача данных с компонентов в окно игры
\item\textbf{Тайминг} 10-15 мин
\item\textbf{Действия преподавателя}
\begin{itemize}
\item Выдать задание студентам;
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\end{itemize}
\item\textbf{Задание}:
\begin{itemize}
\itemВ методе обработчика нажатия кнопки необходимо заменить константы в аргументе вызова метода старта игры на текущие показания компонентов (какая радио-кнопка активна, значение слайдера размеров поля, значение слайдера выигрышной длины).
\textbf{Вариант решения}
При решении этого задания важно понимать, что любое невозможное состояние компонента нельзя игнорировать и должно быть выведено сообщение об ошибке, идеально, если будет выброшено исключение. Необходимо пояснить студентам, что это делается для самого же разработчика, который спустя условные полгода не вспомнит, куда именно нужно добавлять код при разработке новой функциональности, например, нового режима игры, а исключение подскажет конкретную строку. Также в представленном ниже варианте решения используются константы, которые нужно добавить в класс \code{Map}.Почему именно в \code{Map}? потому что именно этот класс занимается логикой игры и если мы хотим добавить новый режим, например, игру в крестики-нолики втроём, то всю логику этого режима будет знать \code{Map}, а, следовательно, и константы с режимами должен задавать он.
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Если группа студентов справилась с заданием, а времени осталось более 5 минут, выдавать группе задания «со звёздочкой».
\end{itemize}
\item\textbf{Задание}:
\begin{itemize}
\item Создать простейшее окно управления сервером (по сути, любым), содержащее две кнопки (\code{JButton}) -- запустить сервер и остановить сервер. Кнопки должны просто логировать нажатие (имитировать запуск и остановку сервера, соответственно) и выставлять внутри интерфейса соответствующее булево \code{isServerWorking}.
\textbf{Вариант решения}
В этом задании нет никаких подводных камней, просто экран, две кнопки, два обработчика
System.out.println("Server started " + isServerWorking);
}
});
setDefaultCloseOperation(EXIT_ON_CLOSE);
setBounds(POS_X, POS_Y, WIDTH, HEIGHT);
setResizable(false);
setTitle("Chat server");
setAlwaysOnTop(true);
setLayout(new GridLayout(1, 2));
add(btnStart);
add(btnStop);
setVisible(true);
}
}
\end{lstlisting}
\item [$*_1$] Если сервер не запущен, кнопка остановки должна сообщить, что сервер не запущен и более ничего не делать. Если сервер запущен, кнопка старта должна сообщить, что сервер работает и более ничего не делать.
\textbf{Вариант решения}
Это задание также без подводных камней, важно не забыть, что после выдачи сообщения в проверке нужно прекратить выполнение обработчика.
System.out.println("Server started " + isServerWorking);
}
});
\end{lstlisting}
\item [$*_2$] Добавить на окно компонент \code{JtextArea} и выводить сообщения сервера в него, а не в терминал.
\textbf{Вариант решения}
В данном задании важно, что при добавлении текстовой области изменяется лейаут окна (обратно на стандартный), а кнопки довольно логично смотрятся сверху или снизу на отдельной панели с компоновкой сеткой. Также все выводы в консоль заменяются на вызов метода \code{log.append()}, и это поведение желательно убрать в приватный метод, что-то вроде \code{putLog(String msg)}, потому что сейчас мы хотим логировать так, а дальше захотим иначе и не надо будет искать во всём коде -- что и как логируется.
\item Подробно объяснить, что именно требуется от студентов, избегая упоминания конкретных языковых конструкций;
\item Если группа студентов справилась с заданием, а времени осталось более 5 минут, выдавать группе задания «со звёздочкой».
\end{itemize}
\item\textbf{Задание}:
\begin{itemize}
\item Создать окно клиента чата. Окно должно содержать \code{JtextField} для ввода логина, пароля, IP-адреса сервера, порта подключения к серверу, область ввода сообщений, \code{JTextArea} область просмотра сообщений чата и \code{JButton} подключения к серверу и отправки сообщения в чат. Желательно сразу сгруппировать компоненты, относящиеся к серверу сгруппировать на \code{JPanel} сверху экрана, а компоненты, относящиеся к отправке сообщения -- на \code{JPanel} снизу.
\textbf{Вариант решения}
К решению, как и к проверке желательно подойти последовательно, сначала создать пустое окно с размерами и со стандартными элементами вроде заголовка и обработки закрытия окна на крестик
private final JPanel panelBottom = new JPanel(new BorderLayout());
private final JTextField tfMessage = new JTextField();
private final JButton btnSend = new JButton("Send");
// constructor
panelBottom.add(tfMessage, BorderLayout.CENTER);
panelBottom.add(btnSend, BorderLayout.EAST);
add(panelBottom, BorderLayout.SOUTH);
\end{lstlisting}
И как без внимания оставить лог. Его нужно сделать не редактируемым и прокручиваемым. для этого вызовем метод \code{setEditable} и добавим в \code{ScrollBox}.
\item [$*_1$] Добавить на экран компонент \code{JList<String>} -- имитацию списка пользователей, заполнить этот список несколькими выдуманными именами пользователей чата. Подсказка: компонент не может добавлять или убирать элементы списка, он работает с методом \code{setListData()}, изучите его аргументы.
\textbf{Вариант решения}
В самом создании списка нет ничего необычного, кроме использования метода из подсказки. Также достаточно важно, что список необходимо поместить в область прокрутки, поскольку список пользователей чата может не поместиться на экран.
если какой-то из пользователей внезапно выберет достаточно длинное имя, оно может перекрыть область вывода сообщений, это обусловлено приоритетом компоновки (центр имеет наименьший приоритет). Для того, чтобы этого избежать нужно для области прокрутки выставить предпочтительный размер (например, ширина 100 и высота не важна).
try (FileWriter out = new FileWriter("log.txt", true)) {
out.write(username + ": " + msg + "\n");
out.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
\end{lstlisting}
\item [15-20 мин] 3. При запуске клиента чата заполнять поле истории из файла, если он существует. Обратите внимание, что чаще всего история сообщений хранится на сервере и заполнение истории чата лучше делать при соединении с сервером, а не при открытии окна клиента.