473,796 Members | 2,740 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Counting poker hands

I was reading http://en.wikipedia.org/wiki/Poker_probability which has a
good description of how to count the frequency of different types of
poker hands using a mathematical approach. A sample Python program is
given in the discussion page for doing it that way. I wanted to take a
different approach and actually generate all the possible hands,
counting the number of each type.

It's quite do-able on today's hardware, with 5-card (2,598,960
combinations) taking only one second and 7-card (133,784,560
combinations) taking around 60 seconds with my program on my notebook.

Does anyone have any suggestions for ways to improve my program,
especially about how to make it run faster? I'd like to get it down to
10 seconds or 5 seconds rather than 60, if that's possible. Profiling
suggests the vast majority of the run time is spent in the 'classify'
function.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define N 7

/* N is the number of cards to deal. The best five-card poker hand
will be selected out of the N cards. Set N to 5 to get the
frequencies for 5-card poker hands as follows:

Straight flush 40
Four of a kind 624
Full house 3,744
Flush 5,108
Straight 10,200
Three of a kind 54,912
Two pair 123,552
One pair 1,098,240
No pair 1,302,540
Total 2,598,960

Or set N to 7 to get the frequences for 7-card poker hands
(as in Texas Hold'em) as follows:

Straight flush 41,584
Four of a kind 224,848
Full house 3,473,184
Flush 4,047,644
Straight 6,180,020
Three of a kind 6,461,620
Two pair 31,433,400
One pair 58,627,800
No pair 23,294,460
Total 133,784,560

Or set N to any other positive number and you should get
appropriate results, given enough time and as long as no
overflow occurs.

*/

#define RANK(card) ((card) % 13)
#define SUIT(card) ((card) / 13)

char *ranks[] = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J",
"Q", "K"};
char *suits[] = {"S", "H", "D", "C"};

/* inc function increments hand to the next combination
of N cards from 52. If already at the last combination,
returns 1 to indicate end, otherwise returns 0.

Algorithm from nextComb function in combEnum.c at
http://reptar.uta.edu/NOTES5314/combEnum.c
*/

int inc(int *hand)
{
int i;

for(i = 0; i < N; i++)
{
if(hand[i] != 52 - N + i) break;
}
if(i == N) return 1;

for(i = N - 1; hand[i] == 52 - N + i; i--);

hand[i]++;

for(i++; i < N; i++)
hand[i] = hand[i - 1] + 1;

return 0;
}

enum class {
STRAIGHT_FLUSH,
FOUR,
FULL_HOUSE,
FLUSH,
STRAIGHT,
THREE,
TWO_PAIR,
ONE_PAIR,
NO_PAIR,
NUM_CLASSES
};

char *class_names[] = {
"Straight flush: ",
"Four of a kind: ",
"Full house: ",
"Flush: ",
"Straight: ",
"Three of a kind: ",
"Two Pair: ",
"One Pair: ",
"No Pair: "
};

void print_hand(int *hand, enum class cl)
{
int i;
for(i = 0; i < N; i++)
{
if(hand[i] < 0 || hand[i] >= 52)
printf("%d ", hand[i]);
else
printf("%s%s ", ranks[RANK(hand[i])], suits[SUIT(hand[i])]);
}
printf(" %s\n", class_names[cl]);
}

int comp(const void *v1, const void *v2)
{
const int *a = v1, *b = v2;
return *a < *b ? -1 : *a *b;
}
/* Even when there is both a straight and a flush in the cards,
they may not be over the same 5-card hand. This function
is called only when there is both a straight and a flush in
the cards, which is fairly rarely, to confirm whether or not
it is actually a straight flush. It operates by sorting the
cards in numerical order (which is suit-major, ensuring that
same-suited cards are together), then checking for a run of
5 adjacent cards each one greater than the last.

The variable 'state' represents the number of adjacent
straight-flush cards seen so far in the current run. It is
incremented when the current card is one greater than the
previous card, and of the same suit.

The hand is considered to contain a straight flush when state
reaches 4, or when state reaches 3 on a king and there is
an ace of the current suit.
*/

int confirm_straigh t_flush(int *hand)
{
int i, state = 0, tmp[N], hasAce[4] = {0, 0, 0, 0};

for(i = 0; i < N; i++)
{
if(RANK(hand[i]) == 0) hasAce[SUIT(hand[i])] = 1;
}

memcpy(tmp, hand, sizeof tmp);
qsort(tmp, N, sizeof *tmp, comp);

for(i = 1; i < N; i++)
{
if(( tmp[i] == tmp[i - 1] + 1
) && SUIT(tmp[i]) == SUIT(tmp[i - 1]))
{
state++;
}
else
{
state = 0;
}
if(state == 4 || (RANK(tmp[i]) == 12 && state == 3 &&
hasAce[SUIT(tmp[i])])) return 1;
}
return 0;
}

enum class classify(int *hand)
{
int rank_counts[13] = {0};
int suit_counts[4] = {0};
int i, flush = 0, straight = 0, state = 0;
int max_rank_count = 0, sub_rank_count = 0;
for(i = 0; i < N; i++)
{
rank_counts[RANK(hand[i])]++;
suit_counts[SUIT(hand[i])]++;
}
for(i = 0; i < 4; i++)
{
if(suit_counts[i] >= 5) flush = 1;
}
for(i = 0; i < 13; i++)
{
if(rank_counts[i] >= max_rank_count)
{
sub_rank_count = max_rank_count;
max_rank_count = rank_counts[i];
}
else if(rank_counts[i] >= sub_rank_count)
{
sub_rank_count = rank_counts[i];
}
if(rank_counts[i] != 0)
{
state++;
}
else
{
state = 0;
}
if(state == 5) straight = 1;
}
if(state == 4 && rank_counts[0] != 0) straight = 1; /* ace-high */
if(flush && straight && confirm_straigh t_flush(hand)) return
STRAIGHT_FLUSH;
if(max_rank_cou nt >= 4) return FOUR;
if(max_rank_cou nt == 3 && sub_rank_count >= 2) return FULL_HOUSE;
if(flush) return FLUSH;
if(straight) return STRAIGHT;
if(max_rank_cou nt == 3) return THREE;
if(max_rank_cou nt == 2 && sub_rank_count == 2) return TWO_PAIR;
if(max_rank_cou nt == 2) return ONE_PAIR;
return NO_PAIR;
}

void print_counts(lo ng long *counts)
{
int i;
long long total = 0;
for(i = 0; i < NUM_CLASSES; i++)
{
printf("%s%12ll d\n", class_names[i], counts[i]);
total += counts[i];
}
printf("Total: %12lld\n", total);
}

int main(void)
{
int i;
long long counts[NUM_CLASSES] = {0};
int hand[N] = {0};

/* initialise hand to first combination */
for(i = 0; i < N; i++) hand[i] = i;

do
{
counts[classify(hand)]++;
} while(!inc(hand ));
print_counts(co unts);
return 0;
}

--
Simon.
Jan 16 '07
27 5630
Randy Howard wrote:
On Wed, 17 Jan 2007 10:27:10 -0600, Simon Biber wrote
>#ifdef _WIN32
# define FORMAT_LONG_LON G "I64"
#else
# define FORMAT_LONG_LON G "ll"
#endif

...
printf("%" FORMAT_LONG_LON G "d\n", value);

If anyone ever needs a convincing argument for why not to use
Microsoft development tools, the above should suffice.
Or operating systems. See the URL in my sig. for horror story
about Vista.

--
"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews
<http://www.cs.auckland .ac.nz/~pgut001/pubs/vista_cost.txt>
Jan 19 '07 #21
On Fri, 19 Jan 2007 05:15:46 -0600, CBFalconer wrote
(in article <45************ ***@yahoo.com>) :
Randy Howard wrote:
>On Wed, 17 Jan 2007 10:27:10 -0600, Simon Biber wrote
>>#ifdef _WIN32
# define FORMAT_LONG_LON G "I64"
#else
# define FORMAT_LONG_LON G "ll"
#endif

...
printf("%" FORMAT_LONG_LON G "d\n", value);

If anyone ever needs a convincing argument for why not to use
Microsoft development tools, the above should suffice.

Or operating systems.
Very true. I haven't bought anything from Microsoft for a few years
now, and computer issues have been on a very steep decline since I
started weaning people off of Windows. It's quite nice, and I highly
recommend it.
See the URL in my sig. for horror story about Vista.
It's only been posted about 10,000 places, commented on far and wide,
and even made several tech podcasts of late. But thanks for adding
that 10,001st copy. :-)

--
Randy Howard (2reply remove FOOBAR)
"The power of accurate observation is called cynicism by those
who have not got it." - George Bernard Shaw

Jan 19 '07 #22
Randy Howard wrote:
cbfalconer wrote:
.... snip ...
>
Very true. I haven't bought anything from Microsoft for a few years
now, and computer issues have been on a very steep decline since I
started weaning people off of Windows. It's quite nice, and I highly
recommend it.
>See the URL in my sig. for horror story about Vista.

It's only been posted about 10,000 places, commented on far and wide,
and even made several tech podcasts of late. But thanks for adding
that 10,001st copy. :-)
Around here (sparse population) doctors are highly dependant on
images over the internet, as I observed with my recent colon
problems etc. So I have been propagating that to them. My
principal physician has a major system on XP that he curses
regularly. I advised him to refuse any offered upgrades to Vista,
to read the URL, and hound his supplier for a Linux version.
However, his software supplier is located in Seattle, so I suspect
bedmates.

--
"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews
<http://www.cs.auckland .ac.nz/~pgut001/pubs/vista_cost.txt>
Jan 20 '07 #23

"CBFalconer " <cb********@yah oo.comwrote in message
news:45******** *******@yahoo.c om...
Randy Howard wrote:
>cbfalconer wrote:
... snip ...
>>
Very true. I haven't bought anything from Microsoft for a few years
now, and computer issues have been on a very steep decline since I
started weaning people off of Windows. It's quite nice, and I highly
recommend it.
>>See the URL in my sig. for horror story about Vista.

It's only been posted about 10,000 places, commented on far and wide,
and even made several tech podcasts of late. But thanks for adding
that 10,001st copy. :-)

Around here (sparse population) doctors are highly dependant on
images over the internet, as I observed with my recent colon
problems etc. So I have been propagating that to them. My
principal physician has a major system on XP that he curses
regularly. I advised him to refuse any offered upgrades to Vista,
to read the URL, and hound his supplier for a Linux version.
However, his software supplier is located in Seattle, so I suspect
bedmates.
Friends don't let friends buy something *fresh* off the rollers at MS. LS
Jan 20 '07 #24

"Simon Biber" <ne**@ralmin.cc wrote in message
news:45******** **@news.peoplet elecom.com.au.. .
Barry wrote:
><we******@gmai l.comwrote in message
news:11******* *************** @s34g2000cwa.go oglegroups.com. ..
>>Simon Biber wrote:
I was reading http://en.wikipedia.org/wiki/Poker_probability which has
a
good description of how to count the frequency of different types of
poker hands using a mathematical approach. A sample Python program is
given in the discussion page for doing it that way. I wanted to take a
different approach and actually generate all the possible hands,
counting the number of each type.

It's quite do-able on today's hardware, with 5-card (2,598,960
combinations ) taking only one second and 7-card (133,784,560
combinations ) taking around 60 seconds with my program on my notebook.

Does anyone have any suggestions for ways to improve my program,
especially about how to make it run faster? I'd like to get it down to
10 seconds or 5 seconds rather than 60, if that's possible. Profiling
suggests the vast majority of the run time is spent in the 'classify'
function.
Indeed. Please take a look at:

http://www.pobox.com/~qed/poker.zip

It does both 5 and 7 card stud calculations by monotonically mapping
each hand to a score metric. All exact ties are all calculated
correctly, for example. Also the two few bits of the metric correctly
classify the hand into the rough hand categorization (two of a kind,
flush, etc).

(I was also working on a full general heads-up situation evaluator, but
got side tracked. There are non-loop based methods for accelerating
that but it starts getting really really complicated. I would probably
require several days of working at it to get it.)

Paul, I took the OPs program and linked it with pokeref.c and it cut
the execution time from about 190 secs. to 100 secs. on my old
laptop. That included the fact that I undid some of the work done
by masking the score of the hand back out so I could classify
it in the same manner the OP had originally and verify the results.

I did something quite similar. My code can now use Paul's fast
bit-twiddling functions when N is 5 or 7, or revert to my slow function
when N is something else.
#define USE_FAST

#if defined(USE_FAS T) && (N == 7 || N == 5)

int classify(int *hand)
How do you adjust the calls from type 'enum class' to 'int'? LS
Jan 20 '07 #25

"Lane Straatman" <in*****@invali d.netwrote in message
news:12******** *****@corp.supe rnews.com...
>
"Simon Biber" <ne**@ralmin.cc wrote in message
news:45******** **@news.peoplet elecom.com.au.. .
Barry wrote:
<we******@gmail .comwrote in message
news:11******** **************@ s34g2000cwa.goo glegroups.com.. .
Simon Biber wrote:
I was reading http://en.wikipedia.org/wiki/Poker_probability which
has
>>a
good description of how to count the frequency of different types of
poker hands using a mathematical approach. A sample Python program is
given in the discussion page for doing it that way. I wanted to take
a
>>different approach and actually generate all the possible hands,
counting the number of each type.

It's quite do-able on today's hardware, with 5-card (2,598,960
combination s) taking only one second and 7-card (133,784,560
combination s) taking around 60 seconds with my program on my
notebook.
>>>
Does anyone have any suggestions for ways to improve my program,
especially about how to make it run faster? I'd like to get it down
to
>>10 seconds or 5 seconds rather than 60, if that's possible. Profiling
suggests the vast majority of the run time is spent in the 'classify'
function.
Indeed. Please take a look at:

http://www.pobox.com/~qed/poker.zip

It does both 5 and 7 card stud calculations by monotonically mapping
each hand to a score metric. All exact ties are all calculated
correctly, for example. Also the two few bits of the metric correctly
classify the hand into the rough hand categorization (two of a kind,
flush, etc).

(I was also working on a full general heads-up situation evaluator,
but
>got side tracked. There are non-loop based methods for accelerating
that but it starts getting really really complicated. I would
probably
>require several days of working at it to get it.)

Paul, I took the OPs program and linked it with pokeref.c and it cut
the execution time from about 190 secs. to 100 secs. on my old
laptop. That included the fact that I undid some of the work done
by masking the score of the hand back out so I could classify
it in the same manner the OP had originally and verify the results.
I did something quite similar. My code can now use Paul's fast
bit-twiddling functions when N is 5 or 7, or revert to my slow function
when N is something else.
#define USE_FAST

#if defined(USE_FAS T) && (N == 7 || N == 5)

int classify(int *hand)
How do you adjust the calls from type 'enum class' to 'int'? LS

I must be missing something here, enums are ints aren't they?
Jan 20 '07 #26
"Barry" <ba****@nullhig hstream.netwrit es:
"Lane Straatman" <in*****@invali d.netwrote in message
news:12******** *****@corp.supe rnews.com...
"Simon Biber" <ne**@ralmin.cc wrote in message
[snip]
I did something quite similar. My code can now use Paul's fast
bit-twiddling functions when N is 5 or 7, or revert to my slow function
when N is something else.
>
>
#define USE_FAST
>
#if defined(USE_FAS T) && (N == 7 || N == 5)
>
int classify(int *hand)
How do you adjust the calls from type 'enum class' to 'int'? LS

I must be missing something here, enums are ints aren't they?
Enums are integers; they aren't necessarily ints.

C99 6.7.2.2p4:

Each enumerated type shall be compatible with char, a signed
integer type, or an unsigned integer type. The choice of type is
implementation-defined, but shall be capable of representing the
values of all the members of the enumeration.

Enum literals are of type int by definition (<OT>in C; C++ has
different rules</OT>). But the enum type itself can be of any integer
type of the compiler's choosing. In many cases, this doesn't matter,
since different integer types can be freely and implicitly converted
to each other. But pointers cannot. If you have a pointer to an enum
type, and the compiler has chosen to make the enum type compatible
with short, then that pointer is *not* compatible with int*.

--
Keith Thompson (The_Other_Keit h) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Jan 20 '07 #27

"Barry" <ba****@nullhig hstream.netwrot e in message
news:12******** *****@corp.supe rnews.com...
>
"Lane Straatman" <in*****@invali d.netwrote in message
news:12******** *****@corp.supe rnews.com...
>>
"Simon Biber" <ne**@ralmin.cc wrote in message
news:45******* ***@news.people telecom.com.au. ..
Barry wrote:
<we******@gmai l.comwrote in message
news:11******* *************** @s34g2000cwa.go oglegroups.com. ..
Simon Biber wrote:
I was reading http://en.wikipedia.org/wiki/Poker_probability which
has
>>>a
good description of how to count the frequency of different types of
poker hands using a mathematical approach. A sample Python program
is
given in the discussion page for doing it that way. I wanted to take
a
>>>different approach and actually generate all the possible hands,
counting the number of each type.

It's quite do-able on today's hardware, with 5-card (2,598,960
combinations ) taking only one second and 7-card (133,784,560
combinations ) taking around 60 seconds with my program on my
notebook.
>>>>
Does anyone have any suggestions for ways to improve my program,
especially about how to make it run faster? I'd like to get it down
to
>>>10 seconds or 5 seconds rather than 60, if that's possible.
Profiling
suggests the vast majority of the run time is spent in the
'classify'
function.
Indeed. Please take a look at:

http://www.pobox.com/~qed/poker.zip

It does both 5 and 7 card stud calculations by monotonically mapping
each hand to a score metric. All exact ties are all calculated
correctly, for example. Also the two few bits of the metric
correctly
classify the hand into the rough hand categorization (two of a kind,
flush, etc).

(I was also working on a full general heads-up situation evaluator,
but
>>got side tracked. There are non-loop based methods for accelerating
that but it starts getting really really complicated. I would
probably
>>require several days of working at it to get it.)

Paul, I took the OPs program and linked it with pokeref.c and it cut
the execution time from about 190 secs. to 100 secs. on my old
laptop. That included the fact that I undid some of the work done
by masking the score of the hand back out so I could classify
it in the same manner the OP had originally and verify the results.

I did something quite similar. My code can now use Paul's fast
bit-twiddling functions when N is 5 or 7, or revert to my slow function
when N is something else.
#define USE_FAST

#if defined(USE_FAS T) && (N == 7 || N == 5)

int classify(int *hand)
How do you adjust the calls from type 'enum class' to 'int'? LS


I must be missing something here, enums are ints aren't they?
I think it much more likely the case that _I_ am missing something. I think
the bulk of my trouble is in vendor-related questions, and therefore posted
in a different ng:
news:12******** *****@corp.supe rnews.com...
--
LS
Jan 20 '07 #28

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

34
2156
by: Mo Geffer | last post by:
Greetings: I have a question about the output of the sample program in section 1.5.3 Line Counting of K&R, Second Edition. Here's the program: /****************************************/ #include <stdio.h> /* count lines in input */
1
8860
by: Jerry | last post by:
We have a 10-question quiz for kids, each question being a yes or no answer using radio selections. I'd like to keep a current total of yes's and no's at the bottom of the quiz (if the user selects yes to question 1, the total of yes's increases by 1). I've been searching for a while but I'm not sure I'm searching with the right keywords. Can anyone direct me to a source I can review to learn how to do this? Thanks! --
7
13270
by: ibtc209 | last post by:
I just started programming in C, and I need some help with this problem. Your program will read the information about one MiniPoker hand, namely the rank and suit of the hand’s first card, and the rank and suit of its second card. Note that the two cards in a hand may be entered in any order; it’s not necessarily the case that the highest card will appear first, for example. Your program will then determine whether the hand is valid, and...
1
4487
by: Martin Olsen | last post by:
Hi all. I am creating a program which calculates poker odds. The program should look at the visible cards (those on your hand and those on the table) then count the cards needed to improve the hand(eg. how many cards do I need to get a Flush) and then calculate the odds based on those numbers. My problem is that I do not know how I should find the missing cards. One way I could do it is to use programming logic like this
4
8915
by: hardieca | last post by:
Has anyone heard of an open-source .NET engine that calculates the winning and losing percentages of hands? Regards, Chris
15
6482
by: sandy123456 | last post by:
At the moment im trying to write a hand class for a game poker patientnce But when i get to the part having to catergorise the difference of full house straight flush flush four of a kind and straight i got stuck.I need to write boolean methods to return these (stright flush , four of a kind..etc) I can only do a pair and 2 pairs and three of a kind. The following is my code please someone if possible help me thanks import java.util.*; ...
13
2558
by: kinghippo423 | last post by:
Hello Everyone, I did a poker program in Java that essencially finds the strenght of a poker hand created Randomly. My program is doing OK...but I'm pretty sure it can be optimised. This is my results with 1 million hands: Number of hands generated : 1000000 Straight Flush : 0.0012 % In theory : 0.0012%
9
4687
by: teejayem | last post by:
I am looking for some help! I am currently developing a new game for an online community. The game is called Pokino which ishalf bingo and half poker. Wierd eh?! Anyway... The final part of the program needs to evaluate two 5 card poker hands and determine which one out of the two hands wins. I thought rather than create a new class of my own I would see if there was anything
7
5682
by: Extremity | last post by:
Hi, I am taking a intro to C++ course so my knowledge base only limits to areas such as if/else, functions, and recursions. We are creating a program which will determine the probability of Poker Hands, such as Royal Flush, Straight Flush, Four of a Kind and such. However in order to complete this, I need to determine how many different poker hands there are. On pencil and paper, this is relatively easy as its a simple combination problem. ...
0
9685
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9535
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
1
10201
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
10021
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
5454
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5582
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4130
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3744
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2931
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.