Skip to content

Commit 9531954

Browse files
yufenggJon Wayne Parrott
authored and
Jon Wayne Parrott
committed
Channel code samples for python (#285)
1 parent b68ae9c commit 9531954

File tree

4 files changed

+473
-3
lines changed

4 files changed

+473
-3
lines changed

appengine/channel/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
To deploy, run the following command, or see [here](https://cloud.google.com/appengine/docs/python/gettingstartedpython27/uploading) for more details about uploading your application.
2+
3+
`appcfg.py -A <YOUR_PROJECT_ID> update .`
4+

appengine/channel/app.yaml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
application: YOUR_PROJECT_ID
2-
version: 1
1+
module: tictactoe
32
runtime: python27
3+
version: 1
44
api_version: 1
5+
threadsafe: true
56

67
handlers:
78
- url: /.*
8-
script: chatactoe.py
9+
script: chatactoe.app
10+
11+
libraries:
12+
- name: webapp2
13+
version: "2.5.2"
14+
- name: jinja2
15+
version: "2.6"

appengine/channel/chatactoe.py

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# Copyright 2016 Google Inc. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
Channel Tic Tac Toe
16+
This module demonstrates the App Engine Channel API by implementing a
17+
simple tic-tac-toe game.
18+
For more information, see the README.md.
19+
"""
20+
21+
# [START all]
22+
23+
import datetime
24+
import logging
25+
import os
26+
import random
27+
import re
28+
import json
29+
30+
from google.appengine.api import channel
31+
from google.appengine.api import users
32+
from google.appengine.api import app_identity
33+
from google.appengine.ext import db
34+
import jinja2
35+
import webapp2
36+
37+
CLOUD_PROJECT_ID = app_identity.get_application_id()
38+
39+
40+
class Game(db.Model):
41+
"""All the data we store for a game"""
42+
userX = db.UserProperty()
43+
userO = db.UserProperty()
44+
board = db.StringProperty()
45+
moveX = db.BooleanProperty()
46+
winner = db.StringProperty()
47+
winning_board = db.StringProperty()
48+
49+
50+
class Wins():
51+
x_win_patterns = ['XXX......', '...XXX...', '......XXX', 'X..X..X..',
52+
'.X..X..X.', '..X..X..X', 'X...X...X', '..X.X.X..']
53+
54+
o_win_patterns = map(lambda s: s.replace('X', 'O'), x_win_patterns)
55+
56+
x_wins = map(lambda s: re.compile(s), x_win_patterns)
57+
o_wins = map(lambda s: re.compile(s), o_win_patterns)
58+
59+
60+
# [START validate_message_3]
61+
class GameUpdater():
62+
"""Creates an object to store the game's state, and handles validating moves
63+
and broadcasting updates to the game."""
64+
game = None
65+
66+
def __init__(self, game):
67+
self.game = game
68+
69+
def get_game_message(self):
70+
gameUpdate = {
71+
'board': self.game.board,
72+
'userX': self.game.userX.user_id(),
73+
'userO': '' if not self.game.userO else self.game.userO.user_id(),
74+
'moveX': self.game.moveX,
75+
'winner': self.game.winner,
76+
'winningBoard': self.game.winning_board
77+
}
78+
return json.dumps(gameUpdate)
79+
80+
def send_update(self):
81+
message = self.get_game_message()
82+
channel.send_message(
83+
self.game.userX.user_id() + self.game.key().id_or_name(), message)
84+
if self.game.userO:
85+
channel.send_message(self.game.userO.user_id() +
86+
self.game.key().id_or_name(), message)
87+
88+
def check_win(self):
89+
if self.game.moveX:
90+
# O just moved, check for O wins
91+
wins = Wins().o_wins
92+
potential_winner = self.game.userO.user_id()
93+
else:
94+
# X just moved, check for X wins
95+
wins = Wins().x_wins
96+
potential_winner = self.game.userX.user_id()
97+
98+
for win in wins:
99+
if win.match(self.game.board):
100+
self.game.winner = potential_winner
101+
self.game.winning_board = win.pattern
102+
return
103+
104+
def make_move(self, position, user):
105+
if position >= 0 and user == self.game.userX or user == self.game.userO:
106+
if self.game.moveX == (user == self.game.userX):
107+
boardList = list(self.game.board)
108+
if (boardList[position] == ' '):
109+
boardList[position] = 'X' if self.game.moveX else 'O'
110+
self.game.board = "".join(boardList)
111+
self.game.moveX = not self.game.moveX
112+
self.check_win()
113+
self.game.put()
114+
self.send_update()
115+
return
116+
# [END validate_message_3]
117+
118+
119+
# [START validate_message_2]
120+
class GameFromRequest():
121+
game = None
122+
123+
def __init__(self, request):
124+
user = users.get_current_user()
125+
game_key = request.get('g')
126+
if user and game_key:
127+
self.game = Game.get_by_key_name(game_key)
128+
129+
def get_game(self):
130+
return self.game
131+
# [END validate_message_2]
132+
133+
134+
# [START validate_message_1]
135+
class MovePage(webapp2.RequestHandler):
136+
def post(self):
137+
game = GameFromRequest(self.request).get_game()
138+
user = users.get_current_user()
139+
if game and user:
140+
id = int(self.request.get('i'))
141+
GameUpdater(game).make_move(id, user)
142+
# [END validate_message_1]
143+
144+
145+
class OpenedPage(webapp2.RequestHandler):
146+
def post(self):
147+
game = GameFromRequest(self.request).get_game()
148+
GameUpdater(game).send_update()
149+
150+
151+
# [START create_channel_1]
152+
class MainPage(webapp2.RequestHandler):
153+
"""The main UI page, renders the 'index.html' template."""
154+
155+
def get(self):
156+
"""Renders the main page. When this page is shown, we create a new
157+
channel to push asynchronous updates to the client."""
158+
user = users.get_current_user()
159+
game_key = self.request.get('g')
160+
game = None
161+
if user:
162+
if not game_key:
163+
game_key = user.user_id()
164+
game = Game(key_name=game_key,
165+
userX=user,
166+
moveX=True,
167+
board=' ')
168+
game.put()
169+
else:
170+
game = Game.get_by_key_name(game_key)
171+
# if not game.userO:
172+
if not game.userO and game.userX != user:
173+
game.userO = user
174+
game.put()
175+
176+
global CLOUD_PROJECT_ID
177+
game_link = 'https://' + CLOUD_PROJECT_ID + '.appspot.com/?g=' + game_key
178+
179+
if game:
180+
token = channel.create_channel(user.user_id() + game_key)
181+
template_values = {'token': token,
182+
'me': user.user_id(),
183+
'game_key': game_key,
184+
'game_link': game_link,
185+
'initial_message':
186+
GameUpdater(game).get_game_message()}
187+
template = jinja_environment.get_template('index.html')
188+
self.response.out.write(template.render(template_values))
189+
else:
190+
self.response.out.write('No such game')
191+
else:
192+
self.redirect(users.create_login_url(self.request.uri))
193+
# [END create_channel_1]
194+
195+
jinja_environment = jinja2.Environment(
196+
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
197+
198+
app = webapp2.WSGIApplication(
199+
[
200+
('/', MainPage), ('/opened', OpenedPage), ('/move', MovePage)
201+
],
202+
debug=True)
203+
204+
# [END all]

0 commit comments

Comments
 (0)