index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>rsp_select</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<style>
#user {
height: 274px;
}
canvas {
pointer-events: none;
position: fixed;
top: 0;
transform: scale(1.1);
}
.myButton {
box-shadow: inset 0px 1px 0px 0px #bbdaf7;
background: linear-gradient(to bottom, #79bbff 5%, #378de5 100%);
background-color: #79bbff;
border-radius: 6px;
border: 1px solid #84bbf3;
display: inline-block;
cursor: pointer;
color: #ffffff;
font-family: Arial;
font-size: 15px;
font-weight: bold;
padding: 6px 24px;
text-decoration: none;
text-shadow: 0px 1px 0px #0e80f2;
}
.myButton:hover {
background: linear-gradient(to bottom, #378de5 5%, #79bbff 100%);
background-color: #378de5;
}
.myButton:active {
position: relative;
top: 1px;
}
</style>
</head>
<body>
<nav class="navbar border-bottom border-bottom-dark d-flex justify-content-space-between" data-bs-theme="dark">
<h1></h1>
<h1></h1>
<h1 class="text-center">가위바위보 게임을 해봅시다!</h1>
<div style="padding-left: 20px; padding-right: 20px;">
<text style="padding-right: 10px;">{{ user }}</text>
<a href="{{url_for('signout')}}" class="myButton">signout</a>
<text style="padding: 10px;"></text>
<a href="{{url_for('view')}}" class="myButton">home</a>
</div>
</nav>
<button id="startButton" style="display:none"></button>
<canvas id="canvas" style="display:none"></canvas>
<div class="container text-center">
<div>
<button type="button" class="btn btn-outline-secondary border-0 border-height-100" id="user" name="user"
value="가위" data-bs-toggle="modal" data-bs-target="#resultModal" onclick="RPSSelecter('가위')">
</button>
<button type="button" class="btn btn-outline-secondary border-0" id="user" name="user" value="바위"
data-bs-toggle="modal" data-bs-target="#resultModal" onclick="RPSSelecter('바위')">
</button>
<button type="button" class="btn btn-outline-secondary border-0" id="user" name="user" value="보"
data-bs-toggle="modal" data-bs-target="#resultModal" onclick="RPSSelecter('보')">
</button>
</div>
<div style="margin:50px; font-size:50px; font-weight: 500;">vs</div>
<!-- 컴퓨터 랜덤 가위바위보 -->
<div style="height: 300px;">
<input type="hidden" id="ComputerVal" name="ComputerVal" value="가위" />
<img id="ComputerImg" name="ComputerImg" src="http://itsys.hansung.ac.kr/WebEditor/upload/images/gawi.gif">
</div>
<!-- 결과 모달창 띄우기(KH) -->
<div class="modal fade" id="resultModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
aria-labelledby="resultModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="resultModalLabel">결과</h5>
</div>
<div class="modal-body" id="resultModalBody">
</div>
<div class="modal-footer">
<p class="text-muted small">계속하시려면 바깥 공간을 클릭하세요.</p>
</div>
</div>
</div>
</div>
<!-- 사용자 랭킹 모달 button -->
<div class="container text-center">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#topUsersModal"
style="float: right;">
사용자 순위 보기
</button>
</div>
<!-- 사용자 랭킹 모달 -->
<div class="modal fade" id="topUsersModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
aria-labelledby="topUsersModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="topUsersModalLabel">Top Users</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<table class="table table-hover">
<thead>
<tr>
<th scope="col" width="10%">순위</th>
<th scope="col" width="20%">사용자</th>
<th scope="col" width="15%">승</th>
<th scope="col" width="15%">패</th>
<th scope="col" width="15%">무</th>
</tr>
</thead>
<tbody>
{% for user in ranking %}
<tr>
<th scope="row">{{ loop.index }}</th>
<td>{{ user.username }}</td>
<td>{{ user.win }}</td>
<td>{{ user.lose }}</td>
<td>{{ user.draw }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div style="text-align: left;">
<button type="button" class="btn btn-primary font-weight-bold">승: {{ reports.win }}</button>
<button type="button" class="btn btn-danger font-weight-bold">패: {{ reports.lose }}</button>
<button type="button" class="btn btn-success font-weight-bold">무: {{ reports.draw }}</button>
</div>
<table class="table table-hover">
<thead>
<tr>
<th scope="col" width="10%">#</th>
<th scope="col" width="20%">사용자</th>
<th scope="col" width="20%">컴퓨터</th>
<th scope="col" width="20%">결과</th>
<th scope="col" width="30%">날짜</th>
</tr>
</thead>
<tbody>
{% for data in record %}
<tr>
<th scope="row">{{ loop.index }}</th>
<td>{{ data.user }}</td>
<td>{{ data.computer }}</td>
<td>{{ data.result }}</td>
<td>{{ data.GameDay }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="{{ url_for('static', filename='confetti_v2.js') }}"></script>
<script>
// DB 변경 사항이 페이지에 즉시 반영되지 않는 오류, form 태그를 사용해야하는 요구 사항에 맞지 않음
// Ajax 비동기식 데이터 전송
function RPSSelecter(user) {
var params = {
user: user,
ComputerVal: $("#ComputerVal").val(),
}
$.ajax({
url: '/receive/data/',
type: 'POST',
data: params,
success: function (response) {
// 모달에 결과 표시(KH)
var resultText = `사용자 : ${response.user}\n컴퓨터 : ${response.computer}\n결과 : ${response.result}`;
document.getElementById('resultModalBody').innerText = resultText;
// 승리 시 이벤트
if (response.result == '승') {
$("#startButton").trigger("click");
$('#canvas').show();
}
// 3초 뒤에 새로고침
setTimeout(function () {
window.location.reload();
}, 3000);
},
error: function (error) {
console.log('Error:', error);
}
});
}
// 가위바위보 이미지
var RPSList = [
];
var RPSIndex = 0;
function switchImage() {
RPSIndex = (RPSIndex + 1) % RPSList.length;
document.getElementById('ComputerVal').value = RPSList[RPSIndex].val;
document.getElementById('ComputerImg').src = RPSList[RPSIndex].img;
}
// 0.1초마다 이미지 변경
setInterval(switchImage, 100);
</script>
</body>
</html>
app.py
# import random
import json
from flask import Flask, render_template, request, redirect, url_for, session, jsonify
from datetime import datetime
import os
from flask_sqlalchemy import SQLAlchemy
# DB 코드
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.secret_key = 'qwerklsmjacveio'
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///" + os.path.join(
basedir, "database.db"
)
db = SQLAlchemy(app)
class RPSGame(db.Model):
id = db.Column(db.Integer, primary_key=True)
user = db.Column(db.String(100), primary_key=False)
computer = db.Column(db.String(100), primary_key=False)
result = db.Column(db.String(100), primary_key=False)
GameDay = db.Column(db.String(100), primary_key=False)
username = db.Column(db.String(100), primary_key=False) # 필터로 사용자별로 구분용
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(100), primary_key=False)
password = db.Column(db.String(100), primary_key=False)
class Ranking(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(100), primary_key=False)
win = db.Column(db.Integer, primary_key=False)
lose = db.Column(db.Integer, primary_key=False)
draw = db.Column(db.Integer, primary_key=False)
with app.app_context():
db.create_all()
rsplist = ['가위', '바위', '보'] # 가위바위보 양식 맞는지 비교용
@app.route("/")
def view():
if "userID" in session:
return render_template("main.html", user=session["userID"], data=True)
return render_template("main.html", data=False)
# 'mainpage<a href="/signin">로그인</a><br><a href="/signup">회원가입</a>' # html제작 필요
@app.route("/test")
def test():
if "userID" in session:
return render_template("signout.html", data=session["userID"], login=True)
else:
return render_template("signout.html", login=False)
# 로그인 폼
@app.route("/signin")
def signin():
if "userID" in session:
print("exist")
return redirect(url_for("view"))
return render_template("signin.html")
# 회원가입 폼
@app.route('/signup')
def signup():
return render_template("signup.html")
# 로그인 하면 처리하러 오는곳
@app.route("/signin-data", methods=["POST"])
def signin_data():
username = request.form["username"]
password = request.form["password"]
# 아이디 비번 짝 맞으면 로그인 성공
id = User.query.filter_by(username=username).first()
if id:
print("id exist")
if id.password == password:
print("로그인 성공")
session["userID"] = username
return redirect(url_for('view'))
else:
print("incorrect")
return redirect(url_for("signin"))
else:
print("id not exist")
return redirect(url_for("signin"))
# 회원가입 하면 처리하러 오는곳
@app.route("/signup-data", methods=["POST"])
def signup_data():
username = request.form["username"]
password = request.form["password"]
# 이미 아이디가 있는 경우
if User.query.filter_by(username=username).first():
print("already exist")
return redirect(url_for("signin")) # 아이디가 있으니 로그인 화면으로
# 아이디가 없으면 생성
new_user = User(username=username, password=password)
userReports = Ranking(username=username, win=0, lose=0, draw=0)
db.session.add(userReports)
db.session.add(new_user)
db.session.commit()
print("회원가입 완료")
return redirect(url_for("signin")) # 회원가입 후 로그인하러home으로
# 로그아웃
@app.route('/signout')
def signout():
session.pop("userID")
return redirect(url_for("view"))
@app.route("/game") # 가위바위보 고르는 페이지, 모달에서 처리 했던것 처럼 값을 보냄
def home():
if "userID" not in session:
return redirect(url_for("view"))
print(session["userID"])
record = RPSGame.query.filter_by(username=session["userID"]).all()
# record.reverse() # DB 최근 등록 순으로 불러오기
# 1번부터 출력할지 마지막부터 출력할지 회의
reports = Ranking.query.filter_by(username=session["userID"]).first()
rankList = top_users()
# 전역 변수 reports 읽기 및 참조
return render_template("index.html", record=record, reports=reports, Ranking=rankList, user=session["userID"])
# @app.route('/receive/data/', methods=['POST'])
@app.route(
"/top_users"
) # 상위 10명의 사용자를 표시하는 모달. (버튼 눌러서 모달을 띄우고 다시 닫을 수 있는 방식으로 구현)
def top_users():
# 많이 승리한 사용자 순으로 정렬하며 동점자의 경우 적게 패배한 사용자가 높이 랭킹.
top_users = (
Ranking.query.order_by(
Ranking.win.desc(), Ranking.lose.asc()).limit(10).all()
)
return top_users
@app.route("/receive/data/", methods=["POST"])
def get_data():
today = datetime.now()
user = request.form.get("user")
computer = request.form.get("ComputerVal")
result = ""
userReports = Ranking.query.filter_by(username=session["userID"]).first()
if not userReports: # 첫판이면
userReports = Ranking(username=session["userID"],
win=0, lose=0, draw=0)
if computer == user:
result = "무"
userReports.draw += 1
elif rsplist[rsplist.index(user) - 1] == computer:
result = "승"
userReports.win += 1
elif rsplist[rsplist.index(user) - 2] == computer:
result = "패"
userReports.lose += 1
firstGame = RPSGame.query.filter_by(username=session["userID"]).all()
if firstGame:
firstGame.reverse()
print(firstGame[0].id)
game = RPSGame(
user=user,
computer=computer,
result=result,
GameDay=today.strftime("%Y-%m-%d %H:%M:%S"),
username=session["userID"]
)
db.session.add(userReports)
db.session.add(game)
db.session.commit()
# return redirect(url_for("home"))
return jsonify(user=user, computer=computer, result=result)
if __name__ == "__main__":
app.run(debug=True)
오늘의 회고
팀 과제 발표를 듣는데 다른 팀들도 열심히 하신게 느껴졌다. 나도 좀 더 열심히 해야겠다는 동기부여가 되었다.
코드를 전체적으로 해석해보며 혼자 공부했다.
내일 목표는 자료구조 및 알고리즘 강의 수강
'매일 TIL' 카테고리의 다른 글
[내일배움캠프 19일차] 해시 함수, heap (0) | 2024.07.12 |
---|---|
[내일배움캠프 18일차] 알고리즘&자료구조 시작 (0) | 2024.07.11 |
[내일배움캠프 16일차] 가위바위보 게임 (0) | 2024.07.09 |
[내일배움캠프 15일차] 가위바위보 게임 (0) | 2024.07.08 |
[내일배움캠프 12일차] SQLite, SQLAlchemy (0) | 2024.07.05 |