A First Encounter with SmartPy

A first regular Python script in SmartPy.io

def f(x):
return 2 * x - 3
alert(sum(f(x) for x in range(0, 12)))

A first example: building a TicTacToe game

import smartpy as spclass TicTacToe(sp.Contract):
def __init__(self):
self.init(nbMoves = 0,
winner = 0,
draw = False,
deck = [[0, 0, 0], [0, 0, 0], [0, 0, 0]])
@sp.entryPoint
def play(self, params):
# contract code is coming here
pass

Using a test

import smartpy as sp
class TicTacToe(sp.Contract):
def __init__(self):
self.init(nbMoves = 0,
winner = 0,
draw = False,
deck = [[0, 0, 0], [0, 0, 0], [0, 0, 0]])
@sp.entryPoint
def play(self, params):
# contract code is coming here
pass
if "templates" not in __name__:
@addTest(name = "Test TicTacToe")
def test():
html = ""
# define a contract
c1 = TicTacToe()
# show its representation
html += c1.fullHtml()
setOutput(html)
  • Stripped: shows the state of the smart contract and its entry points, here play which happens to do nothing.
  • SmartPy: like “Stripped” but with a bit more information.
  • Data Only: data only, no entry points.
  • Types: type information about storage and entry points.
  • All: gathers information from other tabs.
  • Michelson: a Michelson script derived from the SmartPy smart contract.
  • X: simply to hide the tabs.
  • The html output is generic, nothing was done for this exact example and it is nonetheless quite readable.
  • Types have been inferred from both script and storage. In Michelson, integers have two different types: int and nat. Here, SmartPy doesn’t know how the user wants to use them so infers intOrNat. Depending on some constructions, it infers int, nat or intOrNat. Similar inference is done for lists, maps, big maps, records, sum types, i.e., or-patterns, etc.
  • The Michelson compiler uses int for both int and intOrNat in SmartPy as this is the most general type and one can always require nat by explicitly using sp.nat(..) in SmartPy.

Going further with play

    @sp.entryPoint
def play(self, params):
self.data.deck[params.i][params.j] = params.move
self.data.nbMoves += 1
import smartpy as spclass TicTacToe(sp.Contract):
def __init__(self):
self.init(nbMoves = 0,
winner = 0,
draw = False,
deck = [[0, 0, 0], [0, 0, 0], [0, 0, 0]])
@sp.entryPoint
def play(self, params):
self.data.deck[params.i][params.j] = params.move
self.data.nbMoves += 1
# Tests
if "templates" not in __name__:
@addTest(name = "Test TicTacToe")
def test():
html = ""
# define a contract
c1 = TicTacToe()
# show its representation
html += c1.fullHtml()
html += h2("Message execution")
html += h3("A first move in the center")
html += c1.play(i = 1, j = 1, move = 1).html()
setOutput(html)
  • Types are inferred for params as well.
  • Some Michelson code is also generated.
  • The contract pretty-printing looks really similar to the Python script.
  • A new box appears as the result of c1.play(i = 1, j = 1, move = 1).html(). It describes the environment used to evaluate play, its arguments and outputs.

Checks

        html   += c1.play(i = 1, j = 1, move = 2).html()
        sp.verify(self.data.deck[params.i][params.j] == 0)
    @sp.entryPoint
def play(self, params):
sp.verify(self.data.deck[params.i][params.j] == 0)
self.data.deck[params.i][params.j] = params.move
self.data.nbMoves += 1
    @sp.entryPoint
def play(self, params):
sp.verify((self.data.winner == 0) & ~self.data.draw)
sp.verify((params.i >= 0) & (params.i < 3))
sp.verify((params.j >= 0) & (params.j < 3))
sp.verify((params.move == 1) | (params.move == 2))
sp.verify(self.data.deck[params.i][params.j] == 0)
self.data.deck[params.i][params.j] = params.move
self.data.nbMoves += 1

Computing Winner or Draw

if "templates" not in __name__:
@addTest(name = "Test TicTacToe")
def test():
html = ""
# define a contract
c1 = TicTacToe()
# show its representation
html += h2("A sequence of interactions with a winner")
html += c1.fullHtml()
html += h2("Message execution")
html += h3("A first move in the center")
html += c1.play(i = 1, j = 1, move = 1).html()
html += h3("A forbidden move")
html += c1.play(i = 1, j = 1, move = 2).html()
html += h3("A second move")
html += c1.play(i = 1, j = 2, move = 2).html()
html += h3("Other moves")
html += c1.play(i = 2, j = 1, move = 1).html()
html += c1.play(i = 2, j = 2, move = 2).html()
# assert int(c1.data.winner) == 0
html += c1.play(i = 0, j = 1, move = 1).html()
# assert int(c1.data.winner) == 1
setOutput(html)
    @sp.entryPoint
def play(self, params):
sp.verify((self.data.winner == 0) & ~self.data.draw)
sp.verify((params.i >= 0) & (params.i < 3))
sp.verify((params.j >= 0) & (params.j < 3))
sp.verify((params.move == 1) | (params.move == 2))
sp.verify(self.data.deck[params.i][params.j] == 0)
self.data.deck[params.i][params.j] = params.move
self.data.nbMoves += 1
r = range(0, 3)
self.checkLine([self.data.deck[params.i][j] for j in r])
self.checkLine([self.data.deck[i][params.j] for i in r])
self.checkLine([self.data.deck[i][i ] for i in r])
self.checkLine([self.data.deck[i][2 - i ] for i in r])
sp.if (self.data.nbMoves == 9) & (self.data.winner == 0):
self.data.draw = True
def checkLine(self, lin):
sp.if ((lin[0]!=0) & (lin[0]==lin[1]) & (lin[0]==lin[2])):
self.data.winner = lin[0]
  • checkLine is a helper method of the contract that serves to check if three aligned cells contain identical and non-zero values.
  • It is called on both line and column of the move currently being played and on both diagonals.
  • Meta-programming is nicely at play here, checkLine is called at creation time of the smart contract, generating the corresponding code. The SmartPy code output in the “Stripped” or “SmartPy” tabs is no longer quasi identical to what was inputted; it is expanded.

Wrapping everything up

import smartpy as spclass TicTacToe(sp.Contract):
def __init__(self):
self.init(nbMoves = 0,
winner = 0,
draw = False,
deck = [[0, 0, 0], [0, 0, 0], [0, 0, 0]])
@sp.entryPoint
def play(self, params):
sp.verify((self.data.winner == 0) & ~self.data.draw)
sp.verify((params.i >= 0) & (params.i < 3))
sp.verify((params.j >= 0) & (params.j < 3))
sp.verify((params.move == 1) | (params.move == 2))
sp.verify(self.data.deck[params.i][params.j] == 0)
self.data.deck[params.i][params.j] = params.move
self.data.nbMoves += 1
r = range(0, 3)
self.checkLine([self.data.deck[params.i][j] for j in r])
self.checkLine([self.data.deck[i][params.j] for i in r])
self.checkLine([self.data.deck[i][i ] for i in r])
self.checkLine([self.data.deck[i][2 - i ] for i in r])
sp.if (self.data.nbMoves == 9) & (self.data.winner == 0):
self.data.draw = True
def checkLine(self, lin):
sp.if ((lin[0]!=0) & (lin[0]==lin[1]) & (lin[0]==lin[2])):
self.data.winner = lin[0]
# Tests
if "templates" not in __name__:
@addTest(name = "Test TicTacToe")
def test():
html = ""
# define a contract
c1 = TicTacToe()
# show its representation
html += h2("A sequence of interactions with a winner")
html += c1.fullHtml()
html += h2("Message execution")
html += h3("A first move in the center")
html += c1.play(i = 1, j = 1, move = 1).html()
html += h3("A forbidden move")
html += c1.play(i = 1, j = 1, move = 2).html()
html += h3("A second move")
html += c1.play(i = 1, j = 2, move = 2).html()
html += h3("Other moves")
html += c1.play(i = 2, j = 1, move = 1).html()
html += c1.play(i = 2, j = 2, move = 2).html()
assert int(c1.data.winner) == 0
html += c1.play(i = 0, j = 1, move = 1).html()
assert int(c1.data.winner) == 1
html += p("Player %i has won" % int(c1.data.winner))
setOutput(html)

To go further

  • Add a new element to the storage called nextPlayer declaring a next player and checking, in play, that this next player plays.
  • Further expanding the design to verify that the sender of the transaction is the correct one by something similar to sp.verify(sp.sender.identity == self.data.nextPlayerAddress). This will be explained in a following tutorial.
  • Players could put some XTZ bound before playing and the winner gets everything. This will be explained and expanded with State Channels in a following post.
  • We could also play another game. Chess and Nim have templates in SmartPy.io. They are not finished in two directions: SmartPy scripts that need to be completed and Michelson compilation that still lacks some constructions. Completing the SmartPy scripts is a wonderful exercice for developers wishing to understand better how it works and get ready for more challenging SmartPy programming. For a more complete Michelson compilation, this is being addressed and will be available soon. Even when incomplete, SmartPy’s Michelson compiler tries its best to show some skeleton of a compiled script and this is already useful.

--

--

--

An intuitive and effective smart contracts language and development platform for Tezos. In Python.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Self Healing Blockchains are the Future

I`d like to share a real-world scenario where a company is in need of trade finance and what…

Blockchain beginner’s guide

TOP Network on Forbes: Secure Bitcoin-Powered Communications Through TOP Network

Drivr Network — advanced A.I. driven rideshare on the blockchain

The wait is OVER! Join the Sinfonia Incentivized Testnet NOW!

QuarkChain Weekly AMA Summary-07/27/2019

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
SmartPy.io

SmartPy.io

An intuitive and effective smart contracts language and development platform for Tezos. In Python.

More from Medium

KYVE / Pool economy/How data are stored

How FUD up is the Market?

CS373 Spring 2022: Abinith Thallam Week 6

Us 0:1 The Surveillance State: Restrictions Get Lifted On Facial Recognition Tech For Policing