Skip to content

Commit

Permalink
Add graphical user interface (GUI) for brick game
Browse files Browse the repository at this point in the history
  • Loading branch information
vitalibo committed Dec 17, 2016
1 parent 9bb3ab1 commit 4657447
Show file tree
Hide file tree
Showing 34 changed files with 960 additions and 2 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@
<version>6.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.2.28</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
25 changes: 23 additions & 2 deletions src/main/java/com/github/vitalibo/brickgame/Run.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
package com.github.vitalibo.brickgame;


import com.github.vitalibo.brickgame.core.ui.BrickGameFrame;

public class Run {

public static void main(String[] args) {
public static void main(String[] args) throws InterruptedException {
BrickGameFrame frame = new BrickGameFrame();
boolean[][] canvas = new boolean[20][10];
int x = 0, y = -1;

while (true) {
if (++y >= 20) {
y = 0;
if (++x >= 10) {
x = 0;
}
}

canvas[y][x] = true;
frame.getFrame().draw(canvas);
canvas[y][x] = false;
frame.getScore().inc();

Thread.sleep(50);
}
}

}
}
31 changes: 31 additions & 0 deletions src/main/java/com/github/vitalibo/brickgame/core/Builder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.github.vitalibo.brickgame.core;

import java.util.function.Consumer;
import java.util.function.Function;

public class Builder<T> {

private final T o;

private Builder(T o) {
this.o = o;
}

public Builder<T> with(Consumer<T> consumer) {
consumer.accept(o);
return this;
}

public <M> Builder<M> map(Function<T, M> function) {
return Builder.of(function.apply(o));
}

public T get() {
return o;
}

public static <T> Builder<T> of(T o) {
return new Builder<>(o);
}

}
8 changes: 8 additions & 0 deletions src/main/java/com/github/vitalibo/brickgame/core/Grid.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.github.vitalibo.brickgame.core;

@FunctionalInterface
public interface Grid {

void draw(boolean[][] src);

}
31 changes: 31 additions & 0 deletions src/main/java/com/github/vitalibo/brickgame/core/Number.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.github.vitalibo.brickgame.core;

public interface Number {

int get();

void set(int value);

default int inc() {
return inc(1);
}

default int inc(int h) {
return Builder.of(get())
.map(v -> v + h)
.with(this::set)
.get();
}

default int dec() {
return dec(1);
}

default int dec(int h) {
return Builder.of(get())
.map(v -> v - h)
.with(this::set)
.get();
}

}
16 changes: 16 additions & 0 deletions src/main/java/com/github/vitalibo/brickgame/core/State.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.github.vitalibo.brickgame.core;

public interface State {

default boolean change() {
return Builder.of(get())
.map(s -> !s)
.with(this::set)
.get();
}

boolean get();

void set(boolean state);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.github.vitalibo.brickgame.core.ui;

import com.github.vitalibo.brickgame.core.Builder;
import lombok.Getter;

import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;

public class BrickGameFrame extends JFrame {

@Getter
private final BrickPanel frame;
@Getter
private final BrickPanel support;
@Getter
private final NumberPanel score;
@Getter
private final NumberPanel speed;
@Getter
private final NumberPanel level;
@Getter
private final IconPanel sound;
@Getter
private final IconPanel pause;

public BrickGameFrame() {
super("Brick Game");
this.frame = new BrickPanel(10, 20);
this.support = new BrickPanel(4, 4);
this.score = new NumberPanel(6);
this.speed = new NumberPanel(2, 18);
this.level = new NumberPanel(2, 18);
this.sound = Builder.of(new IconPanel("sound_on", "sound_off", true))
.with(i -> i.setPreferredSize(new Dimension(16, 16)))
.get();
this.pause = Builder.of(new IconPanel("pause_on", "pause_off"))
.with(i -> i.setPreferredSize(new Dimension(40, 13)))
.get();
this.init();
}

private void init() {
this.setBackground(new Color(0x6D785C));
this.setContentPane(root());
this.pack();
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
SwingUtilities.updateComponentTreeUI(this);
} catch (Exception e) {
throw new RuntimeException(e);
}

this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setSize(new Dimension(190, 260));
this.setResizable(false);
Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation((int) ((dimension.getWidth() - this.getWidth()) / 2),
(int) ((dimension.getHeight() - this.getHeight()) / 2));
this.setVisible(true);
}

private JPanel root() {
return Builder.of(new JPanel())
.with(root -> root.setOpaque(false))
.with(root -> Builder.of(frame)
.with(frame -> frame.setBorder(new LineBorder(new Color(0x0), 1)))
.with(root::add))
.with(root -> panel(new GridLayout(0, 1))
.with(panel -> panel(new GridLayout(0, 1))
.with(o -> o.add(label("Score", JLabel.RIGHT)))
.with(o -> o.add(score))
.with(panel::add))
.with(panel -> panel(new FlowLayout(FlowLayout.CENTER, 0, 0))
.with(o -> o.add(support))
.with(panel::add))
.with(panel -> panel(new GridLayout(0, 1))
.with(o -> o.add(label("Speed", JLabel.CENTER)))
.with(o -> o.add(speed))
.with(panel::add))
.with(panel -> panel(new GridLayout(0, 1))
.with(o -> o.add(level))
.with(o -> o.add(label("Level", JLabel.CENTER)))
.with(panel::add))
.with(panel -> panel(new GridLayout(0, 1))
.with(o -> o.add(sound))
.with(o -> o.add(pause))
.with(panel::add))
.with(root::add))
.get();
}

private static JLabel label(String text, int alignment) {
return Builder.of(new JLabel(text))
.with(l -> l.setFont(new Font("Consolas", Font.BOLD, 11)))
.with(l -> l.setHorizontalAlignment(alignment))
.get();
}

private static Builder<JPanel> panel(LayoutManager layout) {
return Builder.of(new JPanel(layout))
.with(p -> p.setOpaque(false));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.github.vitalibo.brickgame.core.ui;

import com.github.vitalibo.brickgame.core.Builder;
import com.github.vitalibo.brickgame.core.Grid;
import com.github.vitalibo.brickgame.core.State;

import javax.swing.*;
import java.awt.*;
import java.util.stream.IntStream;

public class BrickPanel extends JPanel implements Grid {

private static final Color ON = new Color(0x000000);
private static final Color OFF = new Color(0x61705B);

private final Brick[][] bricks;

private final int width;
private final int height;

BrickPanel(final int width, final int height) {
this.width = width;
this.height = height;
this.bricks = IntStream.range(0, height)
.mapToObj(h -> IntStream.range(0, width)
.mapToObj(w -> new Brick())
.peek(this::add)
.toArray(Brick[]::new))
.toArray(v -> new Brick[height][width]);
this.setLayout(Builder.of(new GridLayout(0, width))
.with(l -> l.setHgap(1))
.with(l -> l.setVgap(1))
.get());
this.setOpaque(false);
}

@Override
public synchronized void draw(boolean[][] src) {
IntStream.range(0, height)
.forEach(h -> IntStream.range(0, width)
.forEach(w -> bricks[h][w].set(src[h][w])));
}

static class Brick extends JPanel implements State {

private boolean state;

Brick() {
this.setOpaque(false);
}

@Override
public boolean get() {
return state;
}

@Override
public void set(boolean state) {
if (this.state != state) {
this.state = state;
repaint();
}
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Color color = state ? ON : OFF;
g.setColor(color);
g.drawRect(0, 0, 9, 9);
g.setColor(color);
g.fillRect(3, 3, 4, 4);
}

}

}
70 changes: 70 additions & 0 deletions src/main/java/com/github/vitalibo/brickgame/core/ui/IconPanel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.github.vitalibo.brickgame.core.ui;

import com.github.vitalibo.brickgame.core.State;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;

public class IconPanel extends JPanel implements State {

private boolean state;

IconPanel(String on, String off) {
this(on, off, false);
}

IconPanel(String on, String off, boolean state) {
this.state = state;
this.setLayout(new FlowLayout(FlowLayout.CENTER, 1, 1));
this.setOpaque(false);
this.add(new Canvas(IconPanel.resourceAsImage(on), IconPanel.resourceAsImage(off)));
}

@Override
public boolean get() {
return state;
}

@Override
public synchronized void set(boolean state) {
if (this.state != state) {
this.state = state;
repaint();
}
}

@Override
public void setPreferredSize(Dimension preferredSize) {
super.setPreferredSize(preferredSize);
this.getComponent(0).setPreferredSize(preferredSize);
}

private static Image resourceAsImage(String resource) {
try {
return ImageIO.read(ClassLoader.getSystemResourceAsStream(String.format("ui/%s.bmp", resource)));
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private class Canvas extends JPanel {

private final Image ON;
private final Image OFF;

private Canvas(Image ON, Image OFF) {
this.ON = ON;
this.OFF = OFF;
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(state ? ON : OFF, 0, 0, null);
}

}

}
Loading

0 comments on commit 4657447

Please sign in to comment.