Initial commit

This commit is contained in:
2022-04-28 19:47:11 +02:00
commit e2edd3b83a
20 changed files with 617 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="JColor-5.0.1" level="project" />
<orderEntry type="library" name="json-20210307" level="project" />
</component>
</module>
+5
View File
@@ -0,0 +1,5 @@
# A quiz written in java11 for school
Has full singleplayer and hotseat support.
Multiplayer via sockets are also implemented but not working
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
# question;right answer index;answers...
Wie toll ist Microsoft;3;Ganz toll; nicht so toll;schlecht;*kotzgeräusche*
Was ist das beste Betriebsystem;0;Linux;Mac;Windows
1 # question;right answer index;answers...
2 Wie toll ist Microsoft;3;Ganz toll; nicht so toll;schlecht;*kotzgeräusche*
3 Was ist das beste Betriebsystem;0;Linux;Mac;Windows
+3
View File
@@ -0,0 +1,3 @@
# question;right answer index;answers...
Wie toll ist Microsoft;3;Ganz toll; nicht so toll;schlecht;*kotzgeräusche*
Was ist das beste Betriebsystem;0;Linux;Mac;Windows
1 # question;right answer index;answers...
2 Wie toll ist Microsoft;3;Ganz toll; nicht so toll;schlecht;*kotzgeräusche*
3 Was ist das beste Betriebsystem;0;Linux;Mac;Windows
+3
View File
@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: org.bytedream.quizzledizzle.Main
+51
View File
@@ -0,0 +1,51 @@
package org.bytedream.quizzledizzle;
import org.bytedream.quizzledizzle.console.Console;
import org.bytedream.quizzledizzle.options.*;
import org.bytedream.quizzledizzle.question.Questions;
import java.io.File;
public class Main {
public static void main(String[] args) throws Exception {
Console console = new Console();
Questions questions = Questions.loadFromCSV(new File("questions.csv"), ";");
while (true) {
Option option;
console.info("1 - Singleplayer");
console.info("2 - Hotseat");
console.info("3 - Host multiplayer game (2 players)");
console.info("4 - Join multiplayer game (2 players)");
switch (console.askInt("Choice: ")) {
case -1:
console.info("Bye, see ya later");
return;
case 1:
option = new Singleplayer(console, questions);
break;
case 2:
option = new Hotseat(console, questions);
break;
case 3:
option = new MultiplayerHost(console, questions);
break;
case 4:
option = new MultiplayerClient(console, questions);
break;
default:
console.error("Please enter a number between 1 and 4 (see options above) or 'q' to exit");
continue;
}
console.clearScreen();
String exitMessage = option.start();
console.clearScreen();
if (exitMessage != null) {
console.error(exitMessage);
}
}
}
}
@@ -0,0 +1,141 @@
package org.bytedream.quizzledizzle.console;
import com.diogonunes.jcolor.Ansi;
import com.diogonunes.jcolor.Attribute;
import org.bytedream.quizzledizzle.question.Question;
import java.util.Scanner;
public class Console {
public static final boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win");
private final Scanner scanner = new Scanner(System.in);
public String ask(String question) {
System.out.print(question);
return scanner.next();
}
public int askInt(String question) {
String maybeNum;
while (true) {
System.out.print(question);
maybeNum = scanner.next();
try {
return Integer.parseInt(maybeNum);
} catch (NumberFormatException e) {
if (maybeNum.equalsIgnoreCase("q")) {
return -1;
} else {
System.out.println(Ansi.colorize("Please enter a number or 'q' to exit", Attribute.RED_TEXT()));
}
}
}
}
public void title(String content) {
System.out.println(content);
}
public void error(String content) {
System.out.println(Ansi.colorize(content, Attribute.RED_TEXT()));
}
public void info(String content) {
System.out.println(Ansi.colorize(content, Attribute.BRIGHT_BLUE_TEXT()));
}
/**
* shows a question
*
* @param question
* @return the answer index
*/
public int showQuestion(Question question) {
System.out.println(Ansi.colorize(question.getQuestion(), Attribute.BLUE_TEXT()));
String[] answers = question.getAnswers();
for (int i = 0; i < answers.length; i++) {
System.out.printf(" -> %d) %s\n", i + 1, answers[i]);
}
while (true) {
System.out.println("");
System.out.print("Enter a number: ");
String answer = scanner.next();
if (answer.equals("q")) {
return -1;
} else {
try {
int chosenAnswer = Integer.parseInt(answer) - 1;
if (chosenAnswer >= 0 && chosenAnswer < answers.length) {
return chosenAnswer;
}
} catch (NumberFormatException ignore) {}
clearLastLine();
clearLastLine();
System.err.println("Please enter a valid number");
}
}
}
/**
* shows the results from a question
*
* @param question
* @param chosenAnswer
*/
public void showResult(Question question, int chosenAnswer) {
String[] answers = question.getAnswers();
char answerEmoji;
for (int i = 0; i < answers.length; i++) {
if (i == chosenAnswer) {
answerEmoji = '✔';
} else {
answerEmoji = '✘';
}
if (i == question.getRightAnswerIndex()) {
System.out.println(Ansi.colorize(String.format("»» %d) %s [%s] ««", i + 1, answers[i], answerEmoji), Attribute.GREEN_TEXT()));
} else {
System.out.println(Ansi.colorize(String.format(" %d) %s [%s]", i + 1, answers[i], answerEmoji), Attribute.RED_TEXT()));
}
}
}
/**
* clears the screen and shows the total questions and the count of right questions
*
* @param totalQuestions
* @param rightQuestions
*/
public void showClearScreen(int totalQuestions, int rightQuestions) {
clearScreen();
System.out.println(Ansi.colorize(String.format("Total questions: %d - Right questions: %d\n", totalQuestions, rightQuestions), Attribute.BLUE_TEXT()));
}
/**
* clears the entire screen.
* repeats \n if the plaform is windows, ansi codes are used on all other plaforms
*/
public void clearScreen() {
if (!isWindows) {
System.out.print("\033[H\033[2J");
} else {
// because windows is windows, ansi codes are not working by default and this has to be used
System.out.println("\n".repeat(1000));
}
}
/**
* clears the last line.
* only works on linux
*/
public void clearLastLine() {
// yep, windows again...
if (!isWindows) {
System.out.print("\033[1F\33[2K");
}
}
}
@@ -0,0 +1,69 @@
package org.bytedream.quizzledizzle.multiplayer;
import org.bytedream.quizzledizzle.question.Question;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Arrays;
import java.util.Random;
public class Client {
private final Socket socket;
private final BufferedReader reader;
private final PrintWriter writer;
private final long seed;
private final Random random;
private final boolean isServer;
public Client(Socket socket, BufferedReader reader, PrintWriter writer, long seed, boolean isServer) {
this.socket = socket;
this.reader = reader;
this.writer = writer;
this.seed = seed;
this.random = new Random(seed);
this.isServer = isServer;
}
public void sendQuestion(Question question) {
writer.printf("%s;%s;%s\n", question.getQuestion(), question.getRightAnswerIndex(), String.join(";", question.getAnswers()));
writer.flush();
}
public Question receiveQuestion() throws IOException {
String[] rawQuestion = reader.readLine().split(";");
return new Question(rawQuestion[0], Integer.parseInt(rawQuestion[1]), Arrays.copyOfRange(rawQuestion, 2, rawQuestion.length));
}
public void sendAnswer(int answerIndex) {
writer.print(answerIndex);
writer.flush();
}
public int receiveAnswer() throws IOException {
return Integer.parseInt(reader.readLine());
}
public void updateSeed(int count) {
this.random.setSeed(seed + count);
}
/**
* Returns if the client has to send the next question to the other player
*
* @return
*/
public boolean requestSend() {
int result = random.nextInt(2);
if (isServer) {
return result == 1;
} else {
return result == 0;
}
}
public void close() throws IOException {
socket.close();
}
}
@@ -0,0 +1,32 @@
package org.bytedream.quizzledizzle.multiplayer;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Connection {
public static Client client(String host, int port) throws IOException {
// creates a new socket an connects directly to the client
Socket socket = new Socket(host, port);
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
long seed = Long.parseLong(reader.readLine());
return new Client(socket, reader, writer, seed, false);
}
public static Client server(int port) throws IOException {
// creates new socket server and waits until a client connects
Socket socket = new ServerSocket(port).accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
// generates and sends a seed which is used to choose if the server or the client sends the questions
long seed = System.nanoTime();
writer.println(seed);
writer.flush();
return new Client(socket, reader, writer, seed, true);
}
}
@@ -0,0 +1,47 @@
package org.bytedream.quizzledizzle.options;
import org.bytedream.quizzledizzle.console.Console;
import org.bytedream.quizzledizzle.question.Question;
import org.bytedream.quizzledizzle.question.Questions;
public class Hotseat extends Option {
public Hotseat(Console console, Questions defaultQuestions) {
super(console, defaultQuestions);
}
@Override
public String start() throws Exception {
int playerCount = console.askInt("Player count> ");
int count = 1;
while (true) {
int[] answers = new int[playerCount];
Question question = defaultQuestions.randomQuestion();
for (int i = 0; i < playerCount; i++) {
console.clearScreen();
console.title("Question " + count);
console.title("Player " + (i + 1));
int answer = console.showQuestion(question);
if (answer == -1) {
return null;
}
answers[i] = answer;
}
console.clearScreen();
console.info("Results");
for (int i = 0; i < playerCount; i++) {
console.info("Player " + (i + 1));
console.showResult(question, answers[i]);
}
if (!console.ask("Continue? [y/N]").toLowerCase().startsWith("y")) {
return null;
}
count++;
}
}
}
@@ -0,0 +1,34 @@
package org.bytedream.quizzledizzle.options;
import org.bytedream.quizzledizzle.console.Console;
import org.bytedream.quizzledizzle.multiplayer.Client;
import org.bytedream.quizzledizzle.multiplayer.Connection;
import org.bytedream.quizzledizzle.question.Questions;
import java.io.IOException;
public class MultiplayerClient extends MultiplayerOption {
public MultiplayerClient(Console console, Questions defaultQuestions) {
super(console, defaultQuestions);
}
@Override
public String start() throws Exception {
String ip = console.ask("IP address to connect to> ");
int port = console.askInt("Port to connect> ");
Client client;
try {
client = Connection.client(ip, port);
console.info("Connected to server");
} catch (IOException e) {
return "Could not connect to the given ip address. Maybe the port or ip is wrong?";
}
while (true) {
if (!this.request(defaultQuestions.randomQuestion(), client)) {
return null;
}
}
}
}
@@ -0,0 +1,36 @@
package org.bytedream.quizzledizzle.options;
import com.diogonunes.jcolor.Ansi;
import org.bytedream.quizzledizzle.console.Console;
import org.bytedream.quizzledizzle.multiplayer.Client;
import org.bytedream.quizzledizzle.multiplayer.Connection;
import org.bytedream.quizzledizzle.question.Questions;
import java.io.IOException;
public class MultiplayerHost extends MultiplayerOption {
public MultiplayerHost(Console console, Questions defaultQuestions) {
super(console, defaultQuestions);
}
@Override
public String start() throws Exception {
int port = console.askInt("Hosting port> ");
console.info(Ansi.colorize("Waiting for new connection on port " + port));
Client client;
try {
client = Connection.server(port);
console.info("Client connected");
} catch (IOException e) {
return "Could not create socket. Maybe the port is out of bounce or already taken?";
}
while (true) {
if (!this.request(defaultQuestions.randomQuestion(), client)) {
return null;
}
}
}
}
@@ -0,0 +1,41 @@
package org.bytedream.quizzledizzle.options;
import org.bytedream.quizzledizzle.console.Console;
import org.bytedream.quizzledizzle.multiplayer.Client;
import org.bytedream.quizzledizzle.question.Question;
import org.bytedream.quizzledizzle.question.Questions;
import java.io.IOException;
public abstract class MultiplayerOption extends Option {
public MultiplayerOption(Console console, Questions defaultQuestions) {
super(console, defaultQuestions);
}
public boolean request(Question question, Client client) throws IOException {
int ownAnswer;
int otherAnswer;
// possible bug: the server could send the question before the client / other player is listening for it
if (client.requestSend()) {
ownAnswer = console.showQuestion(question);
client.sendQuestion(question);
otherAnswer = client.receiveAnswer();
client.sendAnswer(ownAnswer);
} else {
question = client.receiveQuestion();
ownAnswer = console.showQuestion(question);
client.sendAnswer(ownAnswer);
otherAnswer = client.receiveAnswer();
}
if (ownAnswer == -1 || otherAnswer == -1) {
client.close();
return true;
}
this.showResult(question, ownAnswer);
return false;
}
}
@@ -0,0 +1,36 @@
package org.bytedream.quizzledizzle.options;
import org.bytedream.quizzledizzle.console.Console;
import org.bytedream.quizzledizzle.question.Question;
import org.bytedream.quizzledizzle.question.Questions;
public abstract class Option {
protected final Console console;
protected final Questions defaultQuestions;
private int totalQuestions = 0;
private int rightQuestions = 0;
public Option(Console console, Questions defaultQuestions) {
this.console = console;
this.defaultQuestions = defaultQuestions;
}
public void showResult(Question question, int chosenAnswer) {
if (chosenAnswer == question.getRightAnswerIndex()) {
rightQuestions++;
}
console.showResult(question, chosenAnswer);
console.showClearScreen(++totalQuestions, rightQuestions);
}
/**
* Entrypoint when the user has chosen this option
*
* @throws Exception
* @return
*/
public abstract String start() throws Exception;
}
@@ -0,0 +1,23 @@
package org.bytedream.quizzledizzle.options;
import org.bytedream.quizzledizzle.console.Console;
import org.bytedream.quizzledizzle.question.Question;
import org.bytedream.quizzledizzle.question.Questions;
public class Singleplayer extends Option {
public Singleplayer(Console console, Questions defaultQuestions) {
super(console, defaultQuestions);
}
@Override
public String start() throws Exception {
while (true) {
Question question = defaultQuestions.randomQuestion();
int answer = console.showQuestion(question);
if (answer == -1) {
return null;
}
this.showResult(question, answer);
}
}
}
@@ -0,0 +1,30 @@
package org.bytedream.quizzledizzle.question;
public class Question {
private final String question;
private final int rightAnswerIndex;
private final String[] answers;
public Question(String question, int rightAnswerIndex, String... answers) {
this.question = question;
this.rightAnswerIndex = rightAnswerIndex;
this.answers = answers;
}
public String getQuestion() {
if (!question.endsWith("?")) {
return question + '?';
}
return question;
}
public int getRightAnswerIndex() {
return rightAnswerIndex;
}
public String[] getAnswers() {
return answers;
}
}
@@ -0,0 +1,49 @@
package org.bytedream.quizzledizzle.question;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
public class Questions {
private final String[][] questions;
public Questions(String[][] questions) {
this.questions = questions;
}
/**
* shows a random question
*
* @return
*/
public Question randomQuestion() {
String[] chosenQuestion = questions[new Random().nextInt(questions.length)];
return new Question(chosenQuestion[0], Integer.parseInt(chosenQuestion[1]), Arrays.copyOfRange(chosenQuestion, 2, chosenQuestion.length));
}
/**
* loads questions from a .csv file
*
* @param file the file to read from
* @param delimiter the used delimiter
* @return
* @throws IOException if the file does not exist
*/
public static Questions loadFromCSV(File file, String delimiter) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(file));
ArrayList<String[]> items = new ArrayList<>();
String line;
while ((line = reader.readLine()) != null) {
// if a comment is in the file
if (line.startsWith("#")) {
continue;
}
items.add(line.split(delimiter));
}
return new Questions(items.toArray(String[][]::new));
}
}