Initial commit

This commit is contained in:
bytedream 2022-04-28 19:47:11 +02:00
commit e2edd3b83a
20 changed files with 617 additions and 0 deletions

14
QuizzleDizzle.iml Normal file
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
README.md Normal file
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

BIN
lib/JColor-5.0.1.jar Normal file

Binary file not shown.

BIN
lib/json-20210307.jar Normal file

Binary file not shown.

Binary file not shown.

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
questions.csv Normal file
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
src/META-INF/MANIFEST.MF Normal file
View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: org.bytedream.quizzledizzle.Main

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);
}
}
}
}

View File

@ -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");
}
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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++;
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}