Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Visual term project2
1. 비주얼프로그래밍 Term 프로젝트 최종 보고서
(네모 로직 퍼즐)
학과 : 정보통신공학과
조원 : 20173028 전한종
20173030 정정범
담당 교수 : 박 동 규 교수님
제출일 : 2019. 12. 21
2. 1. 프로젝트 주제
자바를 이용하여 네모로직퍼즐을 만들기
1) 이름
네모로직퍼즐
2) 게임설명
네모로직이란? 직사각형으로 배열된 칸들을 숫자를 보고 그리거나 지워나가는 퍼즐
3) 게임방식
- 쓰인 숫자만큼의 연속된 칸을 칠해야 함.
- 숫자와 숫자 사이에는 적어도 한 칸을 비워야 함.
- 숫자의 순서와 칠해진 칸의 순서는 일치해야 함.
+ 힌트 : 힌트 버튼을 누를 시 랜덤으로 가로 혹은 세로 줄의 정답을 알려줌
2. 개발 일정
11월 24일 ~ 11월 27일 - 프로젝트 구상 및 계획
11월 28일 ~ 12월 8일 – 가장 기본적인 알고리즘 코딩(네모로직의 보드판 행과 열 판, 정답
및 마우스 이벤트)
12월 9일 ~ 12월 18일 – 추가적인 구현(보드 크기 조정, 게임탭(New game, Answer, Exit), 드
래그 기능, 등)
12월 19일 ~ 12월 21일 – 피드백 및 최종 점검
3. 역할 분담
비대면이라도 디스코드 등 온라인 메신저를 통하여 회의 및 자신의 아이디어 및 코딩 알고리
즘을 같이 구현하였습니다.
3. 4. 캡쳐 화면
최초 실행 화면 위쪽 탭에는 Game 메뉴가 있고, 왼쪽 상단에는 시작하자 마자 타이머가 돌아감.
시작할 때 기본적으로 입력되어 있는 네모로직 퍼즐이 구현됨.
4. 보드에 마우스를 올릴시 마우스 올린 위치가 색칠되며 행과 열쪽에도 노란색으로 칠해져 가시성
을 높임
열 또는 행을 숫자에 맞게 채울 시 해당 열의 숫자는 파란색으로 표시
드래그 시 몇 칸 칠하는지 빨간색 글씨로 보이며 X, 파란색 칠하기 , 표시 지우기가 가능함.
구별하는 방법은 처음칸이 칠해져있는가 안칠해져있는가에 따라 표시가 지워지거나 표시를 함
5. 정답을 맞출 시 파란색으로 칠해진 것은 검은색으로 칠해지며 곱표를 칠한 부분도 없어짐. 또한
다시 표시 할 수 없도록 마우스이벤트를 조정함. 타이머도 즉시 멈춤
게임 탭에는 New Game, Answer, Exit 항목이 있음
6. NewGame 을 누를시
문제를 선택 할 수 있는 탭이 나옴. 문제는 크기(10*10이든 20*20이든 상관없음)
예를 들어 강아지를 선택하고 열면
타이머는 초기화 되고 해당 문제로 보드가 바뀜
게임 탭에서 Answer를 누를 시 타이머는 멈추고 정답이 나옴
Exit 를 누르면 게임에서 나가짐
7. 5. 느낀 점
처음에 알고리즘을 구현할 때 생각하고 코딩을 하였는데 좀처럼 쉽지 않았다.
생각한 대로 구현되지 않고 오류가 뜨기도 해서 당황 했지만 같이 하니까 이에 대해 이야기하면
서 오류를 고쳐 나가니 수월하게 끝냈다. 공동 작업이니 만큼 어려운 점 이 있으면 조원에게 서
로 물어보며 해결하니까 좋았다.
로직 문제를 만들 때 배열을 직접 입력해야 하는데 사람이 하다보니 30X30과 같은 커다란 로직
퍼즐의 경우 오류가 자주 나서 시간 만 더 있으면 로직 퍼즐을 제작할 수 있는 알고리즘도 구현
하고 싶다.
프로그램을 완성한 후 잘 돌아가는 것을 보니 뿌듯했다.
8. 6. 전체 코드
NemoNemo.java
import javax.swing.*; //스윙 패키지 선언
import java.awt.*; //Font 상수 등을 위한 awt 패키지 선언
import java.awt.event.*;
import java.io.*;
import java.time.Duration;
/* 보드 크기 지정하는 법
* 30 행, 31 행의 NUM_COLUMN, NUM_ROW 를 바꾼다. -> NUM_COLUMN x NUM_ROW, 지금은 10 x 10 으로
되어있음
* 37 행의 data 에 알맞은 값을 넣는다. -> NUM_COLUMN * NUM_ROW 만큼, 지금은 100 자의 0 과 1 로 구성된
문자열임.
* */
public class Nemonemo extends JFrame implements ActionListener{
JPanel contentPane;
JPanel stopwatch;
//메뉴
JMenuBar menuBar = new JMenuBar();
JMenu gameMenu = new JMenu("Game");
//부착(add)할 클래스의 선언
Board board;
Column col;
Row row;
// 길이
int NUM_COLUMN = 20; // 열의 개수
int NUM_ROW = 20; // 행의 개수
//마우스 커서의 좌표
int mouseX = -1;
int mouseY = -1;
String data =
"1111111111111111111110000100000000100001101111111001111111011000010000000010000110111111100111
11110110000000000000000001101111111001111111011010000010010000010110111111100111111101100001000
00000100001101111111001111111011000010000000010000110111111100111111101101000001001000001011011
11111001111111011000000000000000000111111111111111111111100010001000100010011111111111111111111
110100010001000100011"; //문제의 정답
int[] temp; //플레이어가 입력한 답
int columnNums[][]; //해당 열에 연속한 '1'의 개수를 표시
int numOfColumn[]; //'0'으로 끊어진 연속한 1 의 개수가 몇 개인가를 표시
int rowNums[][]; //해당 행에 연속한 '1'의 개수를 표시
9. int numOfRow[]; //'0'으로 끊어진 연속한 1 의 개수가 몇 개인가를 표시
boolean endFlag = false; //퍼즐이 풀렸는지 여부
public static void main(String args[]) //Nemonemo 애플리케이션 시작점
{
Nemonemo nemo = new Nemonemo(); //네모네모로직 게임 생성
nemo.setVisible(true);
nemo.toFront();
}
public Nemonemo()
{
this.setTitle("LOGIC"); //애플리케이션 창의 타이틀 설정
this.setSize(121+20*(NUM_COLUMN+1), 170+20*(NUM_ROW+1));
//애플리케이션 크기 설정
//변수 초기화
temp = new int[NUM_COLUMN * NUM_ROW]; //가로 10 칸, 세로
10 칸으로 총 100 칸 선언
for(int i=0; i<(NUM_COLUMN * NUM_ROW); i++) //플레이어가
입력하기 전에 0 으로 모두 초기화
{
temp[i] = 0;
}
columnNums = new int[NUM_COLUMN][NUM_COLUMN];
numOfColumn = new int[NUM_COLUMN];
rowNums = new int[NUM_ROW][NUM_ROW];
numOfRow = new int[NUM_ROW];
contentPane = (JPanel) getContentPane();
contentPane.setBackground(Color.white);
contentPane.setLayout(null); //null 레이아웃으로 설정
createMenus(); //메뉴 생성
//column 생성
col = new Column(this);
contentPane.add(col);
col.setFont(new Font("SansSerif", Font.BOLD, 14));
col.setBounds(120, 0, 20*NUM_COLUMN+1, 120);
col.repaint();
//row 생성
row = new Row(this);
contentPane.add(row);
row.setFont(new Font("SansSerif", Font.BOLD, 14));
row.setBounds(0, 120, 120, 20*NUM_ROW+1);
10. //board 생성
board = new Board(this);
contentPane.add(board);
board.setFont(new Font("SansSerif", Font.BOLD, 14));
board.setBounds(120, 120, 20*NUM_COLUMN+1,
20*NUM_ROW+1);
//타이머 생성
stopwatch = new StopWatchPane();
stopwatch.setBackground(Color.white);
contentPane.add(stopwatch);
stopwatch.setBounds(0, 0, 119, 119);
}
public void createMenus()
{
this.setJMenuBar(menuBar);
menuBar.add(gameMenu);
//Game 메뉴의 서부메뉴 생성
JMenuItem newGame = new JMenuItem("New Game ...");
newGame.addActionListener(this);
newGame.setActionCommand("newGame");
gameMenu.add(newGame);
JMenuItem answerGame = new JMenuItem("Answer");
answerGame.addActionListener(this);
answerGame.setActionCommand("answerGame");
gameMenu.add(answerGame);
JMenuItem exitGame = new JMenuItem("Exit");
exitGame.addActionListener(this);
exitGame.setActionCommand("exitGame");
gameMenu.add(exitGame);
}
public void showLocation(int mouseX, int mouseY) //마우스 커서의위치를
표시
{
if(mouseX!=this.mouseX) //마우스 커서가 위치한 열이 변한 경우
{
this.mouseX = mouseX;
col.repaint();
}
if(mouseY!=this.mouseY) //마우스 커서가 위치한 행이 변한 경우
{
this.mouseY = mouseY;
row.repaint();
11. }
}
public void display() //퍼즐이 풀렸는지 여부를 검사
{ // a 번째 col, b 번째 row 의 값 = (COL * row) + col
boolean endFlag = true;
for(int j=0; (j<NUM_ROW)&&endFlag; j++)
for(int i=0; (i<NUM_COLUMN)&&endFlag; i++)
{
if((data.charAt(j*NUM_COLUMN+i)=='1')&&(temp[j*NUM_COLUMN+i]!=1))
endFlag=false; //채워야 할
칸을 모두채웠는지 검사
else
if((data.charAt(j*NUM_COLUMN+i)!='1')&&(temp[j*NUM_COLUMN+i]==1))
endFlag=false; //채우지
않아야 할 칸을채웠는지 검사
}
if(endFlag)
{
this.endFlag = endFlag;
board.repaint(); //퍼즐이 다 풀렸으면 보드의 칸을
채움
StopWatchPane.timer.stop();
}
}
public void actionPerformed(ActionEvent e) //선택한 메뉴에 따라실행할
루틴을 호출
{
String cmd = e.getActionCommand();
if(cmd.equals("newGame")) //네모네모로직 데이터를 불러와 새
게임을 시작
showOpenDialog();
else if(cmd.equals("answerGame")) //Answer 를 선택하면정답을
출력
{
this.endFlag = true;
board.repaint();
StopWatchPane.timer.stop();
}
else if(cmd.equals("exitGame")) //게임 종료
this.dispose();
}
//메뉴에서 New Game 선택 시, 퍼즐 데이터를 불러오는 메소드
public void showOpenDialog()
{
12. FileDialog fd = new FileDialog(this, "Open a File",
FileDialog.LOAD);
fd.setFile("*.txt"); //데이터 파일의 확장자는 txt
fd.setVisible(true);
if(fd.getFile()!=null)
{
String filename = fd.getFile();
String logicDir = fd.getDirectory();
if(filename.indexOf('.')!=-1)
{
filename = (filename.substring(0,
filename.indexOf('.'))).toLowerCase();
}
else
{
filename = filename.toLowerCase();
}
String logicName = filename;
File f;
FileInputStream from = null;
BufferedReader d = null;
try
{
f = new File(logicDir + logicName +
".txt");
from = new FileInputStream(f);
d = new BufferedReader(new
InputStreamReader(from));
int count_col = 0;
int count_row = 0;
data = d.readLine();
for(int i = 0; i < data.length(); i++) {
char tmpchar = data.charAt(i);
if(count_col == 0)
count_row ++;
if(tmpchar == ' ')
count_col ++;
}
NUM_COLUMN = count_col + 1;
NUM_ROW = count_row - 1;
data = data.replace(" ", "");
13. Column col;
Row row;
this.setTitle("LOGIC"); //애플리케이션 창의
타이틀 설정
this.setSize(121+20*(NUM_COLUMN+1),
170+20*(NUM_ROW+1)); //애플리케이션 크기 설정
//변수 초기화
temp = new int[NUM_COLUMN *
NUM_ROW]; //가로 10 칸, 세로 10 칸으로 총 100 칸 선언
for(int i=0; i<(NUM_COLUMN *
NUM_ROW); i++) //플레이어가 입력하기 전에 0 으로 모두 초기화
{
temp[i] = 0;
}
columnNums = new
int[NUM_COLUMN][NUM_COLUMN];
numOfColumn = new int[NUM_COLUMN];
rowNums = new
int[NUM_ROW][NUM_ROW];
numOfRow = new int[NUM_ROW];
contentPane = (JPanel) getContentPane();
contentPane.setBackground(Color.white);
contentPane.setLayout(null); //null
레이아웃으로 설정
//column 생성
col = new Column(this);
contentPane.add(col);
col.setFont(new Font("SansSerif",
Font.BOLD, 14));
col.setBounds(120, 0,
20*NUM_COLUMN+1, 120);
col.repaint();
//row 생성
row = new Row(this);
contentPane.add(row);
row.setFont(new Font("SansSerif",
Font.BOLD, 14));
row.setBounds(0, 120, 120,
20*NUM_ROW+1);
//board 생성
board = new Board(this);
contentPane.add(board);
14. board.setFont(new Font("SansSerif",
Font.BOLD, 14));
board.setBounds(120, 120,
20*NUM_COLUMN+1, 20*NUM_ROW+1);
//타이머 생성
StopWatchPane.timer.stop();
stopwatch = new StopWatchPane();
stopwatch.setBackground(Color.white);
contentPane.add(stopwatch);
stopwatch.setBounds(0, 0, 119, 119);
d.close();
}
catch(IOException e)
{
System.out.println("I/O ERROR: "+ e);
}
//변수 초기화
for(int i=0; i<NUM_COLUMN*NUM_ROW; i++)
{
temp[i] = 0;
}
this.endFlag = false;
//불러온 데이터에 맞춰 column, row 의 숫자를
재생성하고 깨끗한 보드를 다시 출력
col.getColumn();
row.getRow();
board.repaint();
}
}
public static class StopWatchPane extends JPanel {
private JLabel label;
private long lastTickTime;
private static Timer timer;
public StopWatchPane() {
setLayout(new GridBagLayout());
label = new JLabel(String.format("%04d:%02d:%02d.%03d",
0, 0, 0, 0));
timer = new Timer(100, new ActionListener() {
19. int y = e.getY();
if((x/20)>=parent.NUM_COLUMN) return;
if((y/20)>=parent.NUM_ROW) return;
if(parent.endFlag)return;
startX = x/20;
startY = y/20;
}
public void mouseReleased(MouseEvent e) //플레이어가 마우스 버튼을 놓은 경우
{
int x = e.getX();
int y = e.getY();
if((x/20)>=parent.NUM_COLUMN) return;
if((y/20)>=parent.NUM_ROW) return;
if(parent.endFlag)return;
if((e.getModifiers() & InputEvent.BUTTON3_MASK)!=0) //마우스 오른쪽 버튼
{
setTemp(x, y, 2);
}
else //마우스 왼쪽 버튼
{
setTemp(x, y, 1);
}
parent.display(); //퍼즐이 풀렸는지 검사
this.drag = false;
repaint();
}
public void mouseMoved(MouseEvent e) //마우스가 움직인 경우
{
int x = e.getX();
int y = e.getY();
if((x/20)>=parent.NUM_COLUMN) return;
if((y/20)>=parent.NUM_ROW) return;
parent.showLocation(x/20, y/20); //column 과 row 에 마우스 커서의 위치를 표시
repaint();
}
public void mouseExited(MouseEvent e) //마우스가 보드를 벗어난 경우
{
parent.showLocation(-1, -1); //column 과 row 에 마우스 커서의 위치를 표시하지 않음
this.drag = false;
repaint();
}
20. public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) //마우스를 드래그한 경우
{
int x = e.getX();
int y = e.getY();
if((x/20)>=parent.NUM_COLUMN) return;
if((y/20)>=parent.NUM_ROW) return;
parent.showLocation(x/20, y/20); //column 과 row 에 마우스 커서의 위치를 표시
this.drag = true;
endX = x/20;
endY = y/20;
repaint();
}
private void setTemp(int x, int y, int value)
//플레이어의 입력을 temp 배열에 저장
{
int i;
if(drag)
{
if(startX==endX)
{
if(startY<endY)
{
if(parent.temp[startX+startY*parent.NUM_COLUMN] !=
0) {
for(i=startY; i<=endY; i++)
parent.temp[startX+i*parent.NUM_COLUMN] = 0;
}
else {
for(i=startY; i<=endY; i++)
parent.temp[startX+i*parent.NUM_COLUMN] = value;
}
}
else if(startY>endY)
{
if(parent.temp[startX+startY*parent.NUM_COLUMN] !=
0) {
for(i=endY; i<=startY; i++)