Compare commits

..

No commits in common. "main" and "master" have entirely different histories.
main ... master

6 changed files with 205 additions and 461 deletions

62
.gitignore vendored
View File

@ -1,62 +0,0 @@
# ---> Eclipse
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
.apt_generated_test/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project

View File

@ -1,3 +0,0 @@
# TicTacToe
Semesteraufgabe von Matias Mas Viehl für Pr1.

View File

@ -9,9 +9,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
@ -19,190 +17,167 @@ import javax.swing.SwingUtilities;
import Logik.TicTacToe;
public class GUI extends JFrame {
public class GUI extends JFrame{
private TicTacToe ttt;
private ArrayList<JButton> buttons = new ArrayList<>();
private JButton neuesSpiel = new JButton();
private JButton konsole = new JButton();
private JButton modus = new JButton();
private boolean spielEnde = false;
public GUI(TicTacToe ttt) {
this.ttt = ttt;
this.setTitle("TicTacToe");
this.setSize(500, 600);
this.setSize(500,600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setLayout(new BorderLayout());
ImageIcon img = new ImageIcon(getClass().getResource("/icon.jpg"));
if (img.getIconWidth() == -1) {
System.out.println("Bild konnte nicht geladen werden!");
} else {
this.setIconImage(img.getImage());
}
fuelleArrayList();
JPanel jp2 = new JPanel();
jp2.setLayout(new GridLayout(3,1));
jp2.setPreferredSize(new Dimension(500, 150));
jp2.setLayout(new BorderLayout());
jp2.setPreferredSize(new Dimension(500,150));
neuesSpiel.setFocusPainted(false);
neuesSpiel.setFont(new Font("Arial", Font.PLAIN, 30));
neuesSpiel.setBackground(new Color(65, 65, 65));
neuesSpiel.setFont(new Font("Arial",Font.PLAIN, 30));
neuesSpiel.setBackground(new Color(65,65,65));
neuesSpiel.setForeground(Color.WHITE);
neuesSpiel.setText("Neues Spiel beginnnen?");
neuesSpiel.setPreferredSize(new Dimension(500, 75));
neuesSpiel.setPreferredSize(new Dimension(500,75));
neuesSpiel.setBorderPainted(false);
neuesSpiel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ttt.resetGame();
resetGui();
revalidate();
repaint();
}
});
jp2.add(neuesSpiel);
modus.setFocusPainted(false);
modus.setFont(new Font("Arial", Font.PLAIN, 30));
modus.setBackground(new Color(90, 90, 90));
modus.setForeground(Color.WHITE);
if(ttt.getModus() == 1) modus.setText("Modus wechseln? (Player vs. Player aktiv)");
else if(ttt.getModus() == 2) modus.setText("Modus wechseln? (Player vs. AI aktiv)");
modus.setPreferredSize(new Dimension(500, 75));
modus.setBorderPainted(false);
modus.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@Override
public void actionPerformed(ActionEvent e) {
ttt.setCounter(0);
ttt.setAktuellerSpieler('X');
spielEnde = false;
ttt.fuelleTreeMap();
if (ttt.getModus() == 2) {
modus.setText("Modus wechseln? (Player vs. Player aktiv)");
ttt.setModus(1);
} else if (ttt.getModus() == 1) {
modus.setText("Modus wechseln? (Player vs. AI aktiv)");
ttt.setModus(2);
}
for (JButton b : buttons) {
for(JButton b: buttons) {
b.setForeground(Color.WHITE);
b.setText("");
}
ttt.resetGame();
//if(ttt.getAktuellerSpieler() == 'O' && ttt.isAiIstDran()) ttt.actionListenerInhalt(buttons.get(ttt.makeTurn()));
revalidate();
repaint();
}
}
});
jp2.add(modus);
jp2.add(neuesSpiel, BorderLayout.NORTH);
konsole.setFocusPainted(false);
konsole.setFont(new Font("Arial", Font.PLAIN, 30));
konsole.setBackground(new Color(65, 65, 65));
konsole.setFont(new Font("Arial",Font.PLAIN, 30));
konsole.setBackground(new Color(90,90,90));
konsole.setForeground(Color.WHITE);
konsole.setText("Zurück zur Konsole?");
konsole.setPreferredSize(new Dimension(500, 75));
konsole.setPreferredSize(new Dimension(500,75));
konsole.setBorderPainted(false);
konsole.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
dispose();
ttt.ermittleEingabeAnfang();
} catch (Exception e1) {
System.out.println("Eingabe von GUI zur Konsole konnte nicht aufgerufen werden");
}
}
});
jp2.add(konsole);
jp2.add(konsole, BorderLayout.SOUTH);
this.add(jp2, BorderLayout.SOUTH);
this.setVisible(true);
}
private void fuelleArrayList() {
JPanel jp = new JPanel(new GridLayout(3, 3));
for (int i = 0; i < 9; i++) {
JPanel jp = new JPanel(new GridLayout(3,3));
for(int i = 0; i < 9; i++) {
JButton b = new JButton();
//System.out.println("Button "+i+" wurde erstellt.");
b.setFocusPainted(false);
b.setFont(new Font("Arial", Font.PLAIN, 70));
if (i % 2 == 0) {
b.setBackground(new Color(90, 90, 90));
} else {
b.setBackground(new Color(65, 65, 65));
b.setFont(new Font("Arial",Font.PLAIN, 70));
if(i%2 == 0) {
b.setBackground(new Color(90,90,90));
}else {
b.setBackground(new Color(65,65,65));
}
b.setForeground(Color.WHITE);
b.setBorderPainted(false);
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(() -> {
ttt.actionListenerInhalt(b);
//System.out.println("Button "+buttons.indexOf(b)+" wurde gedrückt!");
});
}
@Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(() -> {
actionListenerInhalt(b);
});
}
});
b.setVisible(true);
buttons.add(b);
jp.add(b);
this.add(jp);
}
}
public void resetGui(){
private void actionListenerInhalt(JButton b) {
for (JButton b : buttons) {
int index = buttons.indexOf(b);
if (!ttt.pruefeBelegtesFeld(index) && !spielEnde) {
ttt.belegteFelder.put(index, ttt.getAktuellerSpieler());
b.setText("" + ttt.getAktuellerSpieler());
b.setForeground(Color.WHITE);
b.setText("");
if (ttt.getCounter() >= 4 && ttt.pruefeSieg()) {
spielEnde = true;
int[] gewinnerIndexe = ttt.getGewinnerIndexe();
}
}
for (int i = 0; i < 3; i++) {
buttons.get(gewinnerIndexe[i]).setForeground(Color.RED);
buttons.get(gewinnerIndexe[i]).setText("" + ttt.getAktuellerSpieler());
}
}
public ArrayList<JButton> getButtons() {
return buttons;
}
ttt.setCounter(ttt.getCounter() + 1);
public void setButtons(ArrayList<JButton> buttons) {
this.buttons = buttons;
if (ttt.getAktuellerSpieler() == 'X') {
ttt.setAktuellerSpieler('O');
} else {
ttt.setAktuellerSpieler('X');
}
repaint();
}
}
}

View File

@ -1,146 +1,73 @@
package Logik;
import java.awt.Color;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.Random;
import java.util.Scanner;
import java.util.TreeMap;
import javax.swing.JButton;
import Anzeige.GUI;
public class TicTacToe {
public TreeMap<Integer, Character> belegteFelder = new TreeMap<>();
private int counter;
private GUI gui;
private TreeMap<Integer, Character> belegteFelder = new TreeMap<>();
private char aktuellerSpieler;
private int ausgewaehltesFeld;
private int[] gewinnerIndexe = new int[3];
private int counter, ausgewaehltesFeld, modus;
private char aktuellerSpieler, spielerBeginn;
private boolean aiIstDran;
public TicTacToe() throws Exception {
ermittleEingabeAnfang();
}
public void ermittleEingabeAnfang() throws Exception {
resetGame();
//setzt praktisch werte zurück
fuelleTreeMap();
aktuellerSpieler = 'X';
counter = 0;
Scanner s = new Scanner(System.in);
System.out.println("\nWollen Sie mit einer GUI('gui' eingeben) oder mit der Konsole('konsole' eingeben) spielen?\nZum beenden einfach 'ende' eingeben.");
System.out.print(">>");
System.out.println("\nWollen Sie mit einer GUI('gui' eingeben) oder mit der Konsole('konsole' eingeben) spielen?");
String befehl = s.nextLine();
ermittleModus();
if (befehl.equalsIgnoreCase("gui")) {
if (modus == 1 || modus == 2) {
System.out.println("Um ein Feld für ein Zug auszuwählen muss dieses Feld angeklickt werden...");
System.out.println("Um ein Feld für ein Zug auszuwählen muss dieses Feld angeklickt werden...");
gui = new GUI(this);
if (aktuellerSpieler == 'O' && aiIstDran)
actionListenerInhalt(gui.getButtons().get(makeTurn()));
}
gui = new GUI(this);
} else if (befehl.equalsIgnoreCase("konsole")) {
if(modus == 1) ermittleBeginner();
System.out.println(
"Die Felder sind von 0 bis 8 durchnummerriert.\nUm ein Feld für ein Zug auszuwählen muss die Zahl des Feldes eingegeben werden...");
gibAktuellenStandAus();
System.out.println("Die Felder sind von 0 bis 8 durchnummerriert.\nUm ein Feld für ein Zug auszuwählen muss die Zahl des Feldes eingegeben werden...");
for (int i = 0; i < 9; i++) {
erfasseEingabe();
gibAktuellenStandAus();
if (i >= 4 && pruefeSieg()) break;
if (i >= 4 && werHatGewonnen(belegteFelder) != null)
break;
wechselAktuellerSpieler();
if (aktuellerSpieler == 'X')aktuellerSpieler = 'O';
else aktuellerSpieler = 'X';
}
if (werHatGewonnen(belegteFelder) == 'D')
System.out.println("Unentschieden!");
else
System.out.println(aktuellerSpieler + " hat gewonnen!");
ermittleEingabeAnfang();
} else if (befehl.equalsIgnoreCase("ende")) {
}else if(befehl.equalsIgnoreCase("ende")) {
System.out.println("Spiel wird beendet.");
System.exit(0);
} else {
}else {
System.out.println("Eingabe ungültig!\nVersuchen Sie es erneut.");
ermittleEingabeAnfang();
}
}
private void ermittleModus() {
Scanner s = new Scanner(System.in);
System.out.println(
"Geben Sie die jeweilige Zahl für den Spielmodus ein: (Player vs. Player -> 1), (Player vs. AI -> 2)");
System.out.print(">>");
try {
modus = s.nextInt();
if (modus < 1 || modus > 3) {
throw new InputMismatchException();
}
} catch (InputMismatchException ime) {
System.out.println("Es wurde keine gültige Eingabe getätigt!");
ermittleModus();
}
}
private void ermittleBeginner() {
Scanner s = new Scanner(System.in);
System.out.println("Wer soll beginnen? (X oder O eingeben...)");
System.out.print(">>");
try {
char eingabe = s.next().toUpperCase().charAt(0);
if (eingabe == 'X' || eingabe == 'O')
aktuellerSpieler = eingabe;
else
throw new Exception();
} catch (Exception e) {
System.out.println("Gib genau ein X oder O ein...");
ermittleBeginner();
}
/*
* else if(modus == 2) {
* System.out.println("Soll der Spieler oder die AI beginnen?");
* System.out.print(">>"); String befehl = s.nextLine(); try {
* if(befehl.equalsIgnoreCase("ai")) { aiIstDran = true;
*
* System.out.println("Die AI beginnt also..."); }else
* if(befehl.equalsIgnoreCase("spieler")) {
* System.out.println("Der Spieler beginnt also..."); spielerBeginn =
* aktuellerSpieler = 'X'; }else { throw new Exception(); } }catch(Exception e)
* { System.out.println("Eingabe konnte nicht verarbeitet werden!");
* ermittleBeginner(); } }
*/
}
private void fuelleTreeMap() {
public void fuelleTreeMap() {
for (int i = 0; i < 9; i++) {
belegteFelder.put(i, (char) (48 + i));
}
@ -149,9 +76,8 @@ public class TicTacToe {
private void gibAktuellenStandAus() {
System.out.println("\n-------------");
for (int i = 0; i < 9; i += 3) {
System.out.printf("|%2s |%2s |%2s |", belegteFelder.get(i), belegteFelder.get(i + 1),
belegteFelder.get(i + 2));
for (int i = 0; i < 9; i+=3) {
System.out.printf("|%2s |%2s |%2s |", belegteFelder.get(i), belegteFelder.get(i+1), belegteFelder.get(i+2));
System.out.println("\n-------------");
}
System.out.println();
@ -164,196 +90,103 @@ public class TicTacToe {
System.out.print("\nSpieler " + aktuellerSpieler + " ist am Zug!\nGeben Sie eine Feldnummer ein:\n>>");
try {
if (modus == 1) {
ausgewaehltesFeld = s.nextInt();
} else if (modus == 2 && !aiIstDran && aktuellerSpieler == 'X') {
ausgewaehltesFeld = s.nextInt();
} else if (modus == 2 && aiIstDran && aktuellerSpieler == 'O') {
ausgewaehltesFeld = makeTurn();
}
ausgewaehltesFeld = s.nextInt();
System.out.println("Die Eingabe: " + ausgewaehltesFeld);
if (pruefeBelegtesFeld(ausgewaehltesFeld))
throw new Exception();
if(pruefeBelegtesFeld(ausgewaehltesFeld)) throw new Exception();
} catch (Exception e) {
System.out.println("Es wurde keine passende Eingabe getätigt,\nversuchen Sie es erneut...");
if (modus == 1 || modus == 2 && aktuellerSpieler == 'X')
erfasseEingabe();
erfasseEingabe();
}
}
public int makeTurn() {
int bestMove = -1;
int bestScore = Integer.MIN_VALUE; // Maximierung für AI (O)
for (int i = 0; i < 9; i++) {
if (belegteFelder.get(i) != 'X' && belegteFelder.get(i) != 'O') {
belegteFelder.put(i, 'O'); // Temporär setzen
int moveScore = minimax(new TreeMap<>(belegteFelder), false);
belegteFelder.put(i, (char) ('0' + i)); // Zug zurücksetzen
if (moveScore > bestScore) {
bestScore = moveScore;
bestMove = i;
}
}
}
return bestMove;
}
private int minimax(TreeMap<Integer, Character> board, boolean isMaximizing) {
Character winner = werHatGewonnen(board);
if (winner != null) {
if (winner == 'X')
return -1;
if (winner == 'O')
return 1;
if (winner == 'D')
return 0;
}
if (isMaximizing) {
int bestScore = Integer.MIN_VALUE;
for (int i = 0; i < 9; i++) {
if (board.get(i) != 'X' && board.get(i) != 'O') {
board.put(i, 'O');
int score = minimax(board, false);
board.put(i, (char) ('0' + i)); // Zug zurücksetzen
bestScore = Math.max(score, bestScore);
}
}
return bestScore;
} else {
int bestScore = Integer.MAX_VALUE;
for (int i = 0; i < 9; i++) {
if (board.get(i) != 'X' && board.get(i) != 'O') {
board.put(i, 'X');
int score = minimax(board, true);
board.put(i, (char) ('0' + i)); // Zug zurücksetzen
bestScore = Math.min(score, bestScore);
}
}
return bestScore;
}
}
private boolean pruefeBelegtesFeld(int index) {
// System.out.println("Zu pruefender Index: " + index+"| Inhalt:
// "+belegteFelder.get(index));
public synchronized boolean pruefeBelegtesFeld(int index) {
System.out.println("Zu pruefender Index: "+index);
if (belegteFelder.get(index) == 'X' || belegteFelder.get(index) == 'O') {
System.out.println("Feld " + index + " schon belegt, machen Sie eine erneute Eingabe...");
return true;
} else {
belegteFelder.put(index, aktuellerSpieler);
}
System.out.println("Feld " + index + " wurde mit " + aktuellerSpieler + " belegt");
return false;
}
private Character werHatGewonnen(TreeMap<Integer, Character> board) {
int[][] gewinnKombinationen = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }, // Reihen
{ 0, 3, 6 }, { 1, 4, 7 }, { 2, 5, 8 }, // Spalten
{ 0, 4, 8 }, { 2, 4, 6 } // Diagonalen
};
for (int[] kombi : gewinnKombinationen) {
if (board.get(kombi[0]) == board.get(kombi[1]) && board.get(kombi[1]) == board.get(kombi[2])
&& (board.get(kombi[0]) == 'X' || board.get(kombi[0]) == 'O')) {
gewinnerIndexe = kombi;
return board.get(kombi[0]); // Gibt 'X' oder 'O' zurück
}
}
// Prüfen, ob noch leere Felder existieren
for (int i = 0; i < 9; i++) {
if (board.get(i) != 'X' && board.get(i) != 'O') {
return null; // Es gibt noch leere Felder → Spiel geht weiter
}
}
return 'D';
}
public void actionListenerInhalt(JButton b) {
int index = gui.getButtons().indexOf(b);
if (werHatGewonnen(belegteFelder) == null && !pruefeBelegtesFeld(index)) {
b.setText("" + aktuellerSpieler);
if (counter >= 4 && werHatGewonnen(belegteFelder) != null) {
if (werHatGewonnen(belegteFelder) != 'D') {
for (int i = 0; i < 3; i++) {
gui.getButtons().get(gewinnerIndexe[i]).setForeground(Color.RED);
gui.getButtons().get(gewinnerIndexe[i]).setText("" + aktuellerSpieler);
}
}
return;
}
counter++;
wechselAktuellerSpieler();
if (modus == 2 && aiIstDran) {
actionListenerInhalt(gui.getButtons().get(makeTurn()));
}
}
}
private void wechselAktuellerSpieler() {
if (aktuellerSpieler == 'X') {
aktuellerSpieler = 'O';
} else {
aktuellerSpieler = 'X';
}
if(modus == 2) aiIstDran = !aiIstDran;
}
public void resetGame() {
counter = 0;
aktuellerSpieler = 'X';
aiIstDran = false;
fuelleTreeMap();
belegteFelder.put(index, aktuellerSpieler);
return false;
}
public int getModus() {
return modus;
public boolean pruefeSieg() {
if ((belegteFelder.get(0) == aktuellerSpieler) && (belegteFelder.get(1) == aktuellerSpieler) && (belegteFelder.get(2) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der ersten Waagerechte und hat somit gewonnen!");
gewinnerIndexe[0] = 0;
gewinnerIndexe[1] = 1;
gewinnerIndexe[2] = 2;
return true;
} else if ((belegteFelder.get(3) == aktuellerSpieler) && (belegteFelder.get(4) == aktuellerSpieler) && (belegteFelder.get(5) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der zweiten Waagerechte und hat somit gewonnen!");
gewinnerIndexe[0] = 3;
gewinnerIndexe[1] = 4;
gewinnerIndexe[2] = 5;
return true;
} else if ((belegteFelder.get(6) == aktuellerSpieler) && (belegteFelder.get(7) == aktuellerSpieler) && (belegteFelder.get(8) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der dritten Waagerechte und hat somit gewonnen!");
gewinnerIndexe[0] = 6;
gewinnerIndexe[1] = 7;
gewinnerIndexe[2] = 8;
return true;
} else if ((belegteFelder.get(0) == aktuellerSpieler) && (belegteFelder.get(3) == aktuellerSpieler) && (belegteFelder.get(6) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der ersten Senkrechte und hat somit gewonnen!");
gewinnerIndexe[0] = 0;
gewinnerIndexe[1] = 3;
gewinnerIndexe[2] = 6;
return true;
} else if ((belegteFelder.get(1) == aktuellerSpieler) && (belegteFelder.get(4) == aktuellerSpieler) && (belegteFelder.get(7) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der zweiten Senkrechte und hat somit gewonnen!");
gewinnerIndexe[0] = 1;
gewinnerIndexe[1] = 4;
gewinnerIndexe[2] = 7;
return true;
} else if ((belegteFelder.get(2) == aktuellerSpieler) && (belegteFelder.get(5) == aktuellerSpieler) && (belegteFelder.get(8) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der dritten Senkrechte und hat somit gewonnen!");
gewinnerIndexe[0] = 2;
gewinnerIndexe[1] = 5;
gewinnerIndexe[2] = 8;
return true;
} else if ((belegteFelder.get(0) == aktuellerSpieler) && (belegteFelder.get(4) == aktuellerSpieler) && (belegteFelder.get(8) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der ersten Diagonale und hat somit gewonnen!");
gewinnerIndexe[0] = 0;
gewinnerIndexe[1] = 4;
gewinnerIndexe[2] = 8;
return true;
} else if ((belegteFelder.get(2) == aktuellerSpieler) && (belegteFelder.get(4) == aktuellerSpieler) && (belegteFelder.get(6) == aktuellerSpieler)) {
System.out.println("Spieler " + aktuellerSpieler + " hat drei '" + aktuellerSpieler+ "' in der zweiten Diagonale und hat somit gewonnen!");
gewinnerIndexe[0] = 2;
gewinnerIndexe[1] = 4;
gewinnerIndexe[2] = 6;
return true;
}
return false;
}
public void setModus(int modus) {
this.modus = modus;
}
public boolean isAiIstDran() {
return aiIstDran;
public void setAktuellerSpieler(char aktuellerSpieler) {
this.aktuellerSpieler = aktuellerSpieler;
}
public char getAktuellerSpieler() {
return aktuellerSpieler;
}
public TreeMap<Integer, Character> getBelegteFelder() {
return belegteFelder;
}
public int[] getGewinnerIndexe() {
return gewinnerIndexe;
}
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
}

View File

@ -1,13 +1,14 @@
package Logik;
public class Main {
public class main {
public static void main(String[] args) {
try {
TicTacToe ttt = new TicTacToe();
} catch (Exception e) {
e.printStackTrace();
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB