FAQ
I'm writing a tetris clone I'm calling "grin" using the goncurses curses
library. The game mostly works and it's pretty playable, but every now and
then garbage will appear on screen. Presumably, this is due to ncurses
lack of support for concurrency, described here:
https://code.google.com/p/goncurses/wiki/Concurrency

It isn't reliably reproducible, but usually comes up in a minute or two of
play. I've tried to work around it using a select statement, and then
using a mutex, but the problem still comes up. I'm new to programming in
Go, so I'm hoping someone can point out where I'm going wrong.

I use a goroutine for the keyboard input and a goroutine for the timer that
causes the block to drop every few ticks. The goroutes each communicate
with the main program through a different channel. The ncurses object is
passed to the keyboard goroutine. The timer goroutine just sleeps.

Here are the relevant parts of my program:

func main() {

// declare variables, etc

// mutex
var nlock struct {
sync.Mutex
}

// keyboard channel - keyboard input comes through here
ck := make(chan int)
go keys_in(stdscr, ck, nlock)

// timer channel - tetris timer to drop the block comes through here
ct := make(chan int)
go t_timer(ct, 1, nlock)

// main loop of the game
for keep_going := true; keep_going == true; {

// wait for either keyboard input or timer
select {
case somechar = <-ct:
action = "timeout"
case somechar = <-ck:
action = "keyboard"
}

nlock.Lock()
// game logic and drawing blocks comes here
// nothing until the end of the loop calls on the channels or the goroutines
nlock.Unlock()

switch {
case action == "timeout":
go t_timer(ct, speed, nlock)
case action == "keyboard":
go keys_in(stdscr, ck, nlock)
}

}

}

func keys_in(stdscr gc.Window, ck chan int, nlock struct{ sync.Mutex }) {
nlock.Lock()
somechar := int(stdscr.GetChar())
ck <- somechar
nlock.Unlock()
}

func t_timer(ct chan int, speed int, nlock struct{ sync.Mutex }) {
mseconds := time.Duration(1000 / speed)
time.Sleep(mseconds * time.Millisecond)
nlock.Lock()
ct <- 110 // drop the block
nlock.Unlock()
}


The whole this is here: https://github.com/nickaubert/grin

Thanks.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Search Discussions

  • Mortdeus at Jul 12, 2013 at 4:42 am
    Use termbox.
    On Thursday, July 11, 2013 8:51:05 PM UTC-5, nicka...@gmail.com wrote:

    I'm writing a tetris clone I'm calling "grin" using the goncurses curses
    library. The game mostly works and it's pretty playable, but every now and
    then garbage will appear on screen. Presumably, this is due to ncurses
    lack of support for concurrency, described here:
    https://code.google.com/p/goncurses/wiki/Concurrency

    It isn't reliably reproducible, but usually comes up in a minute or two of
    play. I've tried to work around it using a select statement, and then
    using a mutex, but the problem still comes up. I'm new to programming in
    Go, so I'm hoping someone can point out where I'm going wrong.

    I use a goroutine for the keyboard input and a goroutine for the timer
    that causes the block to drop every few ticks. The goroutes each
    communicate with the main program through a different channel. The ncurses
    object is passed to the keyboard goroutine. The timer goroutine just
    sleeps.

    Here are the relevant parts of my program:

    func main() {

    // declare variables, etc

    // mutex
    var nlock struct {
    sync.Mutex
    }

    // keyboard channel - keyboard input comes through here
    ck := make(chan int)
    go keys_in(stdscr, ck, nlock)

    // timer channel - tetris timer to drop the block comes through here
    ct := make(chan int)
    go t_timer(ct, 1, nlock)

    // main loop of the game
    for keep_going := true; keep_going == true; {

    // wait for either keyboard input or timer
    select {
    case somechar = <-ct:
    action = "timeout"
    case somechar = <-ck:
    action = "keyboard"
    }

    nlock.Lock()
    // game logic and drawing blocks comes here
    // nothing until the end of the loop calls on the channels or the
    goroutines
    nlock.Unlock()

    switch {
    case action == "timeout":
    go t_timer(ct, speed, nlock)
    case action == "keyboard":
    go keys_in(stdscr, ck, nlock)
    }

    }

    }

    func keys_in(stdscr gc.Window, ck chan int, nlock struct{ sync.Mutex }) {
    nlock.Lock()
    somechar := int(stdscr.GetChar())
    ck <- somechar
    nlock.Unlock()
    }

    func t_timer(ct chan int, speed int, nlock struct{ sync.Mutex }) {
    mseconds := time.Duration(1000 / speed)
    time.Sleep(mseconds * time.Millisecond)
    nlock.Lock()
    ct <- 110 // drop the block
    nlock.Unlock()
    }


    The whole this is here: https://github.com/nickaubert/grin

    Thanks.
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Carlos Castillo at Jul 12, 2013 at 8:29 am
    I concur with the termbox suggestion, and add a useful
    link: http://godoc.org/github.com/nsf/termbox-go

    Unless you need the power of ncurses, termbox-go is simpler, requires no
    external library (ie: it's pure go), is cross platform (linux/osx/windows),
    and much more amenable to concurrency (you should still test for data-races
    though).

    If you want to stick to using goncurses, I also noticed the following:

        - Several methods which take a gc.Window but not the lock. If goncurses
        is not thread-safe, then all ncurses calls should be protected by the mutex
        - You've defined an anonymous struct type that embeds a sync.Mutex, that
        seems redundant and unnecessary.

    My suggestion would be to write your own functions that act on a global
    lock & ncurses objects, or methods on a struct that contain both entities,
    and put the resulting code in a new file. This way your tetris-logic is
    separated from your ncurses code, so it's easier to write both, and ensure
    that you are locking access to ncurses properly. If you create a type with
    a useful interface, you can re-use it in another project (or at least look
    at it easier), or you can build an implementation that uses a different
    backend (say termbox, or OpenGL) and swap it in later.
    On Thursday, July 11, 2013 9:42:54 PM UTC-7, mortdeus wrote:

    Use termbox.
    On Thursday, July 11, 2013 8:51:05 PM UTC-5, nicka...@gmail.com wrote:

    I'm writing a tetris clone I'm calling "grin" using the goncurses curses
    library. The game mostly works and it's pretty playable, but every now and
    then garbage will appear on screen. Presumably, this is due to ncurses
    lack of support for concurrency, described here:
    https://code.google.com/p/goncurses/wiki/Concurrency

    It isn't reliably reproducible, but usually comes up in a minute or two
    of play. I've tried to work around it using a select statement, and then
    using a mutex, but the problem still comes up. I'm new to programming in
    Go, so I'm hoping someone can point out where I'm going wrong.

    I use a goroutine for the keyboard input and a goroutine for the timer
    that causes the block to drop every few ticks. The goroutes each
    communicate with the main program through a different channel. The ncurses
    object is passed to the keyboard goroutine. The timer goroutine just
    sleeps.

    Here are the relevant parts of my program:

    func main() {

    // declare variables, etc

    // mutex
    var nlock struct {
    sync.Mutex
    }

    // keyboard channel - keyboard input comes through here
    ck := make(chan int)
    go keys_in(stdscr, ck, nlock)

    // timer channel - tetris timer to drop the block comes through here
    ct := make(chan int)
    go t_timer(ct, 1, nlock)

    // main loop of the game
    for keep_going := true; keep_going == true; {

    // wait for either keyboard input or timer
    select {
    case somechar = <-ct:
    action = "timeout"
    case somechar = <-ck:
    action = "keyboard"
    }

    nlock.Lock()
    // game logic and drawing blocks comes here
    // nothing until the end of the loop calls on the channels or the
    goroutines
    nlock.Unlock()

    switch {
    case action == "timeout":
    go t_timer(ct, speed, nlock)
    case action == "keyboard":
    go keys_in(stdscr, ck, nlock)
    }

    }

    }

    func keys_in(stdscr gc.Window, ck chan int, nlock struct{ sync.Mutex }) {
    nlock.Lock()
    somechar := int(stdscr.GetChar())
    ck <- somechar
    nlock.Unlock()
    }

    func t_timer(ct chan int, speed int, nlock struct{ sync.Mutex }) {
    mseconds := time.Duration(1000 / speed)
    time.Sleep(mseconds * time.Millisecond)
    nlock.Lock()
    ct <- 110 // drop the block
    nlock.Unlock()
    }


    The whole this is here: https://github.com/nickaubert/grin

    Thanks.
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Nick Aubert at Jul 12, 2013 at 11:44 am
    Termbox looks good. I'll give it a try. Thanks!

    On Fri, Jul 12, 2013 at 4:29 AM, Carlos Castillo wrote:

    I concur with the termbox suggestion, and add a useful link:
    http://godoc.org/github.com/nsf/termbox-go

    Unless you need the power of ncurses, termbox-go is simpler, requires no
    external library (ie: it's pure go), is cross platform (linux/osx/windows),
    and much more amenable to concurrency (you should still test for data-races
    though).

    If you want to stick to using goncurses, I also noticed the following:

    - Several methods which take a gc.Window but not the lock. If
    goncurses is not thread-safe, then all ncurses calls should be protected by
    the mutex
    - You've defined an anonymous struct type that embeds a sync.Mutex,
    that seems redundant and unnecessary.

    My suggestion would be to write your own functions that act on a global
    lock & ncurses objects, or methods on a struct that contain both entities,
    and put the resulting code in a new file. This way your tetris-logic is
    separated from your ncurses code, so it's easier to write both, and ensure
    that you are locking access to ncurses properly. If you create a type with
    a useful interface, you can re-use it in another project (or at least look
    at it easier), or you can build an implementation that uses a different
    backend (say termbox, or OpenGL) and swap it in later.
    On Thursday, July 11, 2013 9:42:54 PM UTC-7, mortdeus wrote:

    Use termbox.
    On Thursday, July 11, 2013 8:51:05 PM UTC-5, nicka...@gmail.com wrote:

    I'm writing a tetris clone I'm calling "grin" using the goncurses curses
    library. The game mostly works and it's pretty playable, but every now and
    then garbage will appear on screen. Presumably, this is due to ncurses
    lack of support for concurrency, described here:
    https://code.google.com/p/**goncurses/wiki/Concurrency<https://code.google.com/p/goncurses/wiki/Concurrency>

    It isn't reliably reproducible, but usually comes up in a minute or two
    of play. I've tried to work around it using a select statement, and then
    using a mutex, but the problem still comes up. I'm new to programming in
    Go, so I'm hoping someone can point out where I'm going wrong.

    I use a goroutine for the keyboard input and a goroutine for the timer
    that causes the block to drop every few ticks. The goroutes each
    communicate with the main program through a different channel. The ncurses
    object is passed to the keyboard goroutine. The timer goroutine just
    sleeps.

    Here are the relevant parts of my program:

    func main() {

    // declare variables, etc

    // mutex
    var nlock struct {
    sync.Mutex
    }

    // keyboard channel - keyboard input comes through here
    ck := make(chan int)
    go keys_in(stdscr, ck, nlock)

    // timer channel - tetris timer to drop the block comes through here
    ct := make(chan int)
    go t_timer(ct, 1, nlock)

    // main loop of the game
    for keep_going := true; keep_going == true; {

    // wait for either keyboard input or timer
    select {
    case somechar = <-ct:
    action = "timeout"
    case somechar = <-ck:
    action = "keyboard"
    }

    nlock.Lock()
    // game logic and drawing blocks comes here
    // nothing until the end of the loop calls on the channels or the
    goroutines
    nlock.Unlock()

    switch {
    case action == "timeout":
    go t_timer(ct, speed, nlock)
    case action == "keyboard":
    go keys_in(stdscr, ck, nlock)
    }

    }

    }

    func keys_in(stdscr gc.Window, ck chan int, nlock struct{ sync.Mutex }) {
    nlock.Lock()
    somechar := int(stdscr.GetChar())
    ck <- somechar
    nlock.Unlock()
    }

    func t_timer(ct chan int, speed int, nlock struct{ sync.Mutex }) {
    mseconds := time.Duration(1000 / speed)
    time.Sleep(mseconds * time.Millisecond)
    nlock.Lock()
    ct <- 110 // drop the block
    nlock.Unlock()
    }


    The whole this is here: https://github.com/nickaubert/**grin<https://github.com/nickaubert/grin>

    Thanks.
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.
  • Kamil Kisiel at Jul 12, 2013 at 6:50 am
    Try compiling your program with the -race flag to see if the race detector
    can help you turn up the source of the problem.

    In general I would suggest designing something like that with a single
    goroutine that deals with everything ncurses-related and communicates with
    other parts of the program via channels.
    On Thursday, July 11, 2013 6:51:05 PM UTC-7, nicka...@gmail.com wrote:

    I'm writing a tetris clone I'm calling "grin" using the goncurses curses
    library. The game mostly works and it's pretty playable, but every now and
    then garbage will appear on screen. Presumably, this is due to ncurses
    lack of support for concurrency, described here:
    https://code.google.com/p/goncurses/wiki/Concurrency

    It isn't reliably reproducible, but usually comes up in a minute or two of
    play. I've tried to work around it using a select statement, and then
    using a mutex, but the problem still comes up. I'm new to programming in
    Go, so I'm hoping someone can point out where I'm going wrong.

    I use a goroutine for the keyboard input and a goroutine for the timer
    that causes the block to drop every few ticks. The goroutes each
    communicate with the main program through a different channel. The ncurses
    object is passed to the keyboard goroutine. The timer goroutine just
    sleeps.

    Here are the relevant parts of my program:

    func main() {

    // declare variables, etc

    // mutex
    var nlock struct {
    sync.Mutex
    }

    // keyboard channel - keyboard input comes through here
    ck := make(chan int)
    go keys_in(stdscr, ck, nlock)

    // timer channel - tetris timer to drop the block comes through here
    ct := make(chan int)
    go t_timer(ct, 1, nlock)

    // main loop of the game
    for keep_going := true; keep_going == true; {

    // wait for either keyboard input or timer
    select {
    case somechar = <-ct:
    action = "timeout"
    case somechar = <-ck:
    action = "keyboard"
    }

    nlock.Lock()
    // game logic and drawing blocks comes here
    // nothing until the end of the loop calls on the channels or the
    goroutines
    nlock.Unlock()

    switch {
    case action == "timeout":
    go t_timer(ct, speed, nlock)
    case action == "keyboard":
    go keys_in(stdscr, ck, nlock)
    }

    }

    }

    func keys_in(stdscr gc.Window, ck chan int, nlock struct{ sync.Mutex }) {
    nlock.Lock()
    somechar := int(stdscr.GetChar())
    ck <- somechar
    nlock.Unlock()
    }

    func t_timer(ct chan int, speed int, nlock struct{ sync.Mutex }) {
    mseconds := time.Duration(1000 / speed)
    time.Sleep(mseconds * time.Millisecond)
    nlock.Lock()
    ct <- 110 // drop the block
    nlock.Unlock()
    }


    The whole this is here: https://github.com/nickaubert/grin

    Thanks.
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/groups/opt_out.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedJul 12, '13 at 4:18a
activeJul 12, '13 at 11:44a
posts5
users4
websitegolang.org

People

Translate

site design / logo © 2022 Grokbase